github.com/wfusion/gofusion@v1.1.14/common/infra/metrics/metrics.go (about)

     1  package metrics
     2  
     3  import (
     4  	"math"
     5  	"strings"
     6  	"time"
     7  
     8  	"github.com/wfusion/gofusion/common/utils"
     9  
    10  	iradix "github.com/hashicorp/go-immutable-radix"
    11  )
    12  
    13  type Label struct {
    14  	Name  string
    15  	Value string
    16  }
    17  
    18  func (m *Metrics) SetGauge(key []string, val float32, opts ...utils.OptionExtender) {
    19  	m.SetGaugeWithLabels(key, val, nil, opts...)
    20  }
    21  
    22  func (m *Metrics) SetGaugeWithLabels(key []string, val float32, labels []Label, opts ...utils.OptionExtender) {
    23  	key, labels, ok := m.formatKeyAndLabels("gauge", key, labels)
    24  	if !ok {
    25  		return
    26  	}
    27  	m.sink.SetGaugeWithLabels(key, val, labels, opts...)
    28  }
    29  
    30  func (m *Metrics) SetPrecisionGauge(key []string, val float64, opts ...utils.OptionExtender) {
    31  	m.SetPrecisionGaugeWithLabels(key, val, nil, opts...)
    32  }
    33  
    34  func (m *Metrics) SetPrecisionGaugeWithLabels(key []string, val float64, labels []Label, opts ...utils.OptionExtender) {
    35  	key, labels, ok := m.formatKeyAndLabels("gauge", key, labels)
    36  	if !ok {
    37  		return
    38  	}
    39  	sink, ok := m.sink.(PrecisionGaugeMetricSink)
    40  	if !ok {
    41  		m.sink.SetGaugeWithLabels(key, float32(val), labels, opts...)
    42  	} else {
    43  		sink.SetPrecisionGaugeWithLabels(key, val, labels, opts...)
    44  	}
    45  }
    46  
    47  func (m *Metrics) EmitKey(key []string, val float32, opts ...utils.OptionExtender) {
    48  	if m.EnableTypePrefix {
    49  		key = insert(0, "kv", key)
    50  	}
    51  	if m.ServiceName != "" {
    52  		key = insert(0, m.ServiceName, key)
    53  	}
    54  	_, allowed := m.allowMetric(key, nil)
    55  	if !allowed {
    56  		return
    57  	}
    58  	m.sink.EmitKey(key, val, opts...)
    59  }
    60  
    61  func (m *Metrics) IncrCounter(key []string, val float32, opts ...utils.OptionExtender) {
    62  	m.IncrCounterWithLabels(key, val, nil, opts...)
    63  }
    64  
    65  func (m *Metrics) IncrCounterWithLabels(key []string, val float32, labels []Label, opts ...utils.OptionExtender) {
    66  	key, labels, ok := m.formatKeyAndLabels("counter", key, labels)
    67  	if !ok {
    68  		return
    69  	}
    70  	m.sink.IncrCounterWithLabels(key, val, labels, opts...)
    71  }
    72  
    73  func (m *Metrics) AddSample(key []string, val float32, opts ...utils.OptionExtender) {
    74  	m.AddSampleWithLabels(key, val, nil, opts...)
    75  }
    76  
    77  func (m *Metrics) AddSampleWithLabels(key []string, val float32, labels []Label, opts ...utils.OptionExtender) {
    78  	key, labels, ok := m.formatKeyAndLabels("sample", key, labels)
    79  	if !ok {
    80  		return
    81  	}
    82  	m.sink.AddSampleWithLabels(key, val, labels, opts...)
    83  }
    84  
    85  func (m *Metrics) AddPrecisionSample(key []string, val float64, opts ...utils.OptionExtender) {
    86  	m.AddPrecisionSampleWithLabels(key, val, nil, opts...)
    87  }
    88  
    89  func (m *Metrics) AddPrecisionSampleWithLabels(key []string, val float64,
    90  	labels []Label, opts ...utils.OptionExtender) {
    91  	key, labels, ok := m.formatKeyAndLabels("sample", key, labels)
    92  	if !ok {
    93  		return
    94  	}
    95  	if sink, ok := m.sink.(PrecisionSampleMetricSink); ok {
    96  		sink.AddPrecisionSampleWithLabels(key, val, labels, opts...)
    97  	} else {
    98  		m.sink.AddSampleWithLabels(key, float32(val), labels, opts...)
    99  	}
   100  }
   101  
   102  func (m *Metrics) MeasureSince(key []string, start time.Time, opts ...utils.OptionExtender) {
   103  	m.MeasureSinceWithLabels(key, start, nil, opts...)
   104  }
   105  
   106  func (m *Metrics) MeasureSinceWithLabels(key []string, start time.Time, labels []Label, opts ...utils.OptionExtender) {
   107  	key, labels, ok := m.formatKeyAndLabels("timer", key, labels)
   108  	if !ok {
   109  		return
   110  	}
   111  	opt := utils.ApplyOptions[Option](opts...)
   112  	sink, ok := m.sink.(PrecisionSampleMetricSink)
   113  	if m.TimerGranularity == 0 {
   114  		if opt.Precision && ok {
   115  			sink.AddPrecisionSampleWithLabels(key, math.MaxFloat64, labels, opts...)
   116  		} else {
   117  			m.sink.AddSampleWithLabels(key, math.MaxFloat32, labels, opts...)
   118  		}
   119  	} else {
   120  		now := time.Now()
   121  		elapsed := now.Sub(start)
   122  		if opt.Precision && ok {
   123  			msec := float64(elapsed.Nanoseconds()) / float64(m.TimerGranularity)
   124  			sink.AddPrecisionSampleWithLabels(key, msec, labels, opts...)
   125  		} else {
   126  			msec := float32(elapsed.Nanoseconds()) / float32(m.TimerGranularity)
   127  			m.sink.AddSampleWithLabels(key, msec, labels, opts...)
   128  		}
   129  	}
   130  }
   131  
   132  // UpdateFilter overwrites the existing filter with the given rules.
   133  func (m *Metrics) UpdateFilter(allow, block []string) {
   134  	m.UpdateFilterAndLabels(allow, block, m.AllowedLabels, m.BlockedLabels)
   135  }
   136  
   137  // UpdateFilterAndLabels overwrites the existing filter with the given rules.
   138  func (m *Metrics) UpdateFilterAndLabels(allow, block, allowedLabels, blockedLabels []string) {
   139  	m.filterLock.Lock()
   140  	defer m.filterLock.Unlock()
   141  
   142  	m.AllowedPrefixes = allow
   143  	m.BlockedPrefixes = block
   144  
   145  	if allowedLabels == nil {
   146  		// Having a white list means we take only elements from it
   147  		m.allowedLabels = nil
   148  	} else {
   149  		m.allowedLabels = make(map[string]bool)
   150  		for _, v := range allowedLabels {
   151  			m.allowedLabels[v] = true
   152  		}
   153  	}
   154  	m.blockedLabels = make(map[string]bool)
   155  	for _, v := range blockedLabels {
   156  		m.blockedLabels[v] = true
   157  	}
   158  	m.AllowedLabels = allowedLabels
   159  	m.BlockedLabels = blockedLabels
   160  
   161  	m.filter = iradix.New()
   162  	for _, prefix := range m.AllowedPrefixes {
   163  		m.filter, _, _ = m.filter.Insert([]byte(prefix), true)
   164  	}
   165  	for _, prefix := range m.BlockedPrefixes {
   166  		m.filter, _, _ = m.filter.Insert([]byte(prefix), false)
   167  	}
   168  }
   169  
   170  func (m *Metrics) Shutdown() {
   171  	if ss, ok := m.sink.(ShutdownSink); ok {
   172  		ss.Shutdown()
   173  	}
   174  }
   175  
   176  func (m *Metrics) formatKeyAndLabels(typePrefix string, keySrc []string, labelsSrc []Label) (
   177  	keyDst []string, labelsDst []Label, ok bool) {
   178  	keyDst = keySrc
   179  	if m.HostName != "" {
   180  		if m.EnableHostnameLabel {
   181  			labelsSrc = append(labelsSrc, Label{"fus_hostname", m.HostName})
   182  		} else if m.EnableHostname {
   183  			keyDst = insert(0, m.HostName, keyDst)
   184  		}
   185  	}
   186  	if m.EnableTypePrefix {
   187  		keyDst = insert(0, typePrefix, keyDst)
   188  	}
   189  	if m.ServiceName != "" {
   190  		if m.EnableServiceLabel {
   191  			labelsSrc = append(labelsSrc, Label{"fus_service", m.ServiceName})
   192  		} else {
   193  			keyDst = insert(0, m.ServiceName, keyDst)
   194  		}
   195  	}
   196  	if m.EnableClientIPLabel {
   197  		labelsSrc = append(labelsSrc, Label{"fus_service_ip", utils.ClientIP()})
   198  	}
   199  
   200  	labelsDst, ok = m.allowMetric(keyDst, labelsSrc)
   201  	return
   202  }
   203  
   204  // labelIsAllowed return true if a should be included in metric
   205  // the caller should lock m.filterLock while calling this method
   206  func (m *Metrics) labelIsAllowed(label *Label) bool {
   207  	labelName := (*label).Name
   208  	if m.blockedLabels != nil {
   209  		_, ok := m.blockedLabels[labelName]
   210  		if ok {
   211  			// If present, let's remove this label
   212  			return false
   213  		}
   214  	}
   215  	if m.allowedLabels != nil {
   216  		_, ok := m.allowedLabels[labelName]
   217  		return ok
   218  	}
   219  	// Allow by default
   220  	return true
   221  }
   222  
   223  // filterLabels return only allowed labels
   224  // the caller should lock m.filterLock while calling this method
   225  func (m *Metrics) filterLabels(labels []Label) []Label {
   226  	if labels == nil {
   227  		return nil
   228  	}
   229  	toReturn := make([]Label, 0, len(labels))
   230  	for _, label := range labels {
   231  		if m.labelIsAllowed(&label) {
   232  			toReturn = append(toReturn, label)
   233  		}
   234  	}
   235  	return toReturn
   236  }
   237  
   238  // Returns whether the metric should be allowed based on configured prefix filters
   239  // Also return the applicable labels
   240  func (m *Metrics) allowMetric(key []string, labels []Label) ([]Label, bool) {
   241  	m.filterLock.RLock()
   242  	defer m.filterLock.RUnlock()
   243  
   244  	if m.filter == nil || m.filter.Len() == 0 {
   245  		return m.filterLabels(labels), m.Config.FilterDefault
   246  	}
   247  
   248  	_, allowed, ok := m.filter.Root().LongestPrefix([]byte(strings.Join(key, ".")))
   249  	if !ok {
   250  		return m.filterLabels(labels), m.Config.FilterDefault
   251  	}
   252  
   253  	return m.filterLabels(labels), allowed.(bool)
   254  }
   255  
   256  // Creates a new slice with the provided string value as the first element
   257  // and the provided slice values as the remaining values.
   258  // Ordering of the values in the provided input slice is kept in tact in the output slice.
   259  func insert(i int, v string, s []string) []string {
   260  	// Allocate new slice to avoid modifying the input slice
   261  	newS := make([]string, len(s)+1)
   262  
   263  	// Copy s[0, i-1] into newS
   264  	for j := 0; j < i; j++ {
   265  		newS[j] = s[j]
   266  	}
   267  
   268  	// Insert provided element at index i
   269  	newS[i] = v
   270  
   271  	// Copy s[i, len(s)-1] into newS starting at newS[i+1]
   272  	for j := i; j < len(s); j++ {
   273  		newS[j+1] = s[j]
   274  	}
   275  
   276  	return newS
   277  }