github.com/alibaba/ilogtail/pkg@v0.0.0-20250526110833-c53b480d046c/protocol/converter/converter_sls_metric.go (about)

     1  // Copyright 2022 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 protocol
    16  
    17  import (
    18  	"fmt"
    19  	"sort"
    20  	"strconv"
    21  	"strings"
    22  	"sync"
    23  	"time"
    24  
    25  	"github.com/alibaba/ilogtail/pkg/protocol"
    26  )
    27  
    28  const (
    29  	metricNameKey      = "__name__"
    30  	metricLabelsKey    = "__labels__"
    31  	metricTimeNanoKey  = "__time_nano__"
    32  	metricValueKey     = "__value__"
    33  	metricValueTypeKey = "__type__"
    34  	metricFieldKey     = "__field__"
    35  )
    36  
    37  const (
    38  	valueTypeFloat  = "float"
    39  	valueTypeInt    = "int"
    40  	valueTypeBool   = "bool"
    41  	valueTypeString = "string"
    42  )
    43  
    44  const (
    45  	KeyValueSeparator = "#$#"
    46  	LabelSeparator    = "|"
    47  )
    48  
    49  var readerPool = sync.Pool{
    50  	New: func() any {
    51  		return &metricReader{}
    52  	},
    53  }
    54  
    55  type metricReader struct {
    56  	name      string
    57  	labels    string
    58  	value     string
    59  	valueType string
    60  	timestamp string
    61  	fieldName string
    62  }
    63  
    64  type MetricLabel struct {
    65  	Key   string
    66  	Value string
    67  }
    68  
    69  type MetricLabels []MetricLabel
    70  
    71  func (m MetricLabels) Len() int {
    72  	return len(m)
    73  }
    74  
    75  func (m MetricLabels) Less(i, j int) bool {
    76  	return m[i].Key < m[j].Key
    77  }
    78  
    79  func (m MetricLabels) Swap(i, j int) {
    80  	m[i], m[j] = m[j], m[i]
    81  }
    82  
    83  func (m MetricLabels) GetLabel() string {
    84  	// sort label
    85  	sort.Sort(m)
    86  	var res []string
    87  	for _, label := range m {
    88  		res = append(res, label.Key+KeyValueSeparator+label.Value)
    89  	}
    90  	return strings.Join(res, LabelSeparator)
    91  }
    92  
    93  func (r *metricReader) readNames() (metricName, fieldName string) {
    94  	if len(r.fieldName) == 0 || r.fieldName == "value" {
    95  		return r.name, "value"
    96  	}
    97  	name := strings.TrimSuffix(r.name, ":"+r.fieldName)
    98  	return name, r.fieldName
    99  }
   100  
   101  func (r *metricReader) readSortedLabels() ([]MetricLabel, error) {
   102  	n := r.countLabels()
   103  	if n == 0 {
   104  		return nil, nil
   105  	}
   106  
   107  	labels := make([]MetricLabel, 0, n)
   108  	remainLabels := r.labels
   109  	lastIndex := -1
   110  	label := ""
   111  	key := ""
   112  
   113  	for len(remainLabels) > 0 {
   114  		endIdx := strings.Index(remainLabels, "|")
   115  		if endIdx < 0 {
   116  			label = remainLabels
   117  			remainLabels = ""
   118  		} else {
   119  			label = remainLabels[:endIdx]
   120  			remainLabels = remainLabels[endIdx+1:]
   121  		}
   122  		splitIdx := strings.Index(label, "#$#")
   123  		if splitIdx < 0 {
   124  			if lastIndex >= 0 {
   125  				labels[lastIndex].Value += "|"
   126  				labels[lastIndex].Value += label
   127  				continue
   128  			}
   129  			if len(key) == 0 {
   130  				key = label
   131  				continue
   132  			}
   133  			key += "|"
   134  			key += label
   135  			continue
   136  		}
   137  
   138  		if len(key) > 0 {
   139  			key += "|"
   140  			key += label[:splitIdx]
   141  		} else {
   142  			key = label[:splitIdx]
   143  		}
   144  
   145  		labels = append(labels, MetricLabel{Key: key, Value: label[splitIdx+3:]})
   146  
   147  		lastIndex++
   148  		key = ""
   149  
   150  		if endIdx < 0 {
   151  			break
   152  		}
   153  	}
   154  
   155  	sort.Sort(MetricLabels(labels))
   156  
   157  	if len(key) > 0 {
   158  		return labels, fmt.Errorf("found miss matching key: %s", key)
   159  	}
   160  
   161  	return labels, nil
   162  }
   163  
   164  func (r *metricReader) countLabels() int {
   165  	if len(r.labels) == 0 {
   166  		return 0
   167  	}
   168  	n := strings.Count(r.labels, "|")
   169  	return n + 1
   170  }
   171  
   172  func (r *metricReader) readValue() (interface{}, error) {
   173  	switch r.valueType {
   174  	case valueTypeBool:
   175  		return strconv.ParseBool(r.value)
   176  	case valueTypeString:
   177  		return r.value, nil
   178  	case valueTypeInt:
   179  		return strconv.ParseInt(r.value, 10, 64)
   180  	default:
   181  		return strconv.ParseFloat(r.value, 64)
   182  	}
   183  }
   184  
   185  func (r *metricReader) readTimestamp() (time.Time, error) {
   186  
   187  	if len(r.timestamp) == 0 {
   188  		return time.Time{}, nil
   189  	}
   190  	t, err := strconv.ParseInt(r.timestamp, 10, 64)
   191  	if err != nil {
   192  		return time.Time{}, err
   193  	}
   194  	return time.Unix(t/1e9, t%1e9).UTC(), nil
   195  }
   196  
   197  func (r *metricReader) recycle() {
   198  	r.reset()
   199  	readerPool.Put(r)
   200  }
   201  
   202  func (r *metricReader) reset() {
   203  	r.labels = ""
   204  	r.name = ""
   205  	r.value = ""
   206  	r.valueType = ""
   207  	r.timestamp = ""
   208  	r.fieldName = ""
   209  }
   210  
   211  func (r *metricReader) set(log *protocol.Log) error {
   212  	r.reset()
   213  	for _, v := range log.Contents {
   214  		switch v.Key {
   215  		case metricNameKey:
   216  			r.name = v.Value
   217  		case metricLabelsKey:
   218  			r.labels = v.Value
   219  		case metricTimeNanoKey:
   220  			r.timestamp = v.Value
   221  		case metricValueKey:
   222  			r.value = v.Value
   223  		case metricValueTypeKey:
   224  			r.valueType = v.Value
   225  		case metricFieldKey:
   226  			r.fieldName = v.Value
   227  		}
   228  	}
   229  	if len(r.name) == 0 || len(r.value) == 0 && r.valueType != valueTypeString {
   230  		return fmt.Errorf("metrics data must contains keys: %s, %s", metricNameKey, metricValueKey)
   231  	}
   232  	return nil
   233  }
   234  
   235  func newMetricReader() *metricReader {
   236  	return readerPool.Get().(*metricReader)
   237  }