github.com/alibaba/ilogtail/pkg@v0.0.0-20250526110833-c53b480d046c/protocol/converter/influxdb_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  	"context"
    19  	"fmt"
    20  	"strings"
    21  	"time"
    22  
    23  	"github.com/influxdata/line-protocol/v2/lineprotocol"
    24  
    25  	"github.com/alibaba/ilogtail/pkg/logger"
    26  	"github.com/alibaba/ilogtail/pkg/models"
    27  	"github.com/alibaba/ilogtail/pkg/protocol"
    28  )
    29  
    30  // ConvertToInfluxdbProtocolStream converts @logGroup to []byte in the influxdb line protocol,
    31  // @c.TagKeyRenameMap, @c.ProtocolKeyRenameMap param will be ignored, as they are not very suitable for metrics.
    32  func (c *Converter) ConvertToInfluxdbProtocolStream(logGroup *protocol.LogGroup, targetFields []string) (stream [][]byte, values []map[string]string, err error) {
    33  	pooledBuf := GetPooledByteBuf()
    34  
    35  	var encoder lineprotocol.Encoder
    36  	encoder.SetBuffer(*pooledBuf)
    37  	encoder.Reset()
    38  	encoder.SetLax(true)
    39  
    40  	reader := newMetricReader()
    41  	defer reader.recycle()
    42  
    43  	for _, log := range logGroup.Logs {
    44  		err := reader.set(log)
    45  		if err != nil {
    46  			return nil, nil, err
    47  		}
    48  
    49  		metricName, fieldName := reader.readNames()
    50  		labels, err := reader.readSortedLabels()
    51  		if err != nil {
    52  			return nil, nil, err
    53  		}
    54  
    55  		v, err := reader.readValue()
    56  		if err != nil {
    57  			return nil, nil, err
    58  		}
    59  		value, ok := lineprotocol.NewValue(v)
    60  		if !ok {
    61  			return nil, nil, fmt.Errorf("unknown field value: %v", v)
    62  		}
    63  		timestamp, err := reader.readTimestamp()
    64  		if err != nil {
    65  			return nil, nil, err
    66  		}
    67  
    68  		encoder.StartLine(metricName)
    69  		for _, v := range labels {
    70  			encoder.AddTag(v.Key, v.Value)
    71  		}
    72  		encoder.AddField(fieldName, value)
    73  		encoder.EndLine(timestamp)
    74  		if encoder.Err() != nil {
    75  			return nil, nil, encoder.Err()
    76  		}
    77  	}
    78  
    79  	// we are batching logs in LogGroup, so only support find tags in the logGroup.LogTags
    80  	var desiredValues map[string]string
    81  	if len(targetFields) > 0 {
    82  		desiredValues = findTargetValuesInLogTags(targetFields, logGroup.LogTags)
    83  	}
    84  
    85  	return [][]byte{encoder.Bytes()}, []map[string]string{desiredValues}, nil
    86  }
    87  
    88  func (c *Converter) ConvertToInfluxdbProtocolStreamV2(groupEvents *models.PipelineGroupEvents, targetFields []string) (stream [][]byte, values []map[string]string, err error) {
    89  	pooledBuf := GetPooledByteBuf()
    90  
    91  	var encoder lineprotocol.Encoder
    92  	encoder.SetBuffer(*pooledBuf)
    93  	encoder.Reset()
    94  	encoder.SetLax(true)
    95  
    96  	for _, event := range groupEvents.Events {
    97  		metric, ok := event.(*models.Metric)
    98  		if !ok {
    99  			if c.IgnoreUnExpectedData {
   100  				logger.Warningf(context.Background(), "CONVERT_ALARM", "unsupported event type[%T] for converter with influxdb protocol", event)
   101  				continue
   102  			}
   103  			return nil, nil, fmt.Errorf("unsupported event type: %v", event.GetType())
   104  		}
   105  		encoder.StartLine(metric.GetName())
   106  		for _, v := range metric.GetTags().SortTo(nil) {
   107  			encoder.AddTag(v.Key, v.Value)
   108  		}
   109  
   110  		v := metric.GetValue()
   111  		if v.IsSingleValue() {
   112  			vv, _ := lineprotocol.FloatValue(v.GetSingleValue())
   113  			encoder.AddField("value", vv)
   114  		} else if v.IsMultiValues() {
   115  			for name, value := range v.GetMultiValues().Iterator() {
   116  				vv, _ := lineprotocol.FloatValue(value)
   117  				encoder.AddField(name, vv)
   118  			}
   119  		}
   120  
   121  		fields := metric.GetTypedValue()
   122  		for name, value := range fields.Iterator() {
   123  			vv, ok := lineprotocol.NewValue(value.Value)
   124  			if !ok {
   125  				return nil, nil, fmt.Errorf("unsupported typed value:%+v", value)
   126  			}
   127  			encoder.AddField(name, vv)
   128  		}
   129  
   130  		t := int64(metric.GetTimestamp())
   131  		encoder.EndLine(time.Unix(t/1e9, t%1e9))
   132  		if encoder.Err() != nil {
   133  			return nil, nil, encoder.Err()
   134  		}
   135  	}
   136  
   137  	// we are batching events in groupEvents, so only support find tags in the groupEvents.Group
   138  	var desiredValues map[string]string
   139  	if len(targetFields) > 0 {
   140  		desiredValues = findTargetFieldsInGroup(targetFields, groupEvents.Group)
   141  	}
   142  
   143  	return [][]byte{encoder.Bytes()}, []map[string]string{desiredValues}, nil
   144  }
   145  
   146  func findTargetValuesInLogTags(targetFields []string, logTags []*protocol.LogTag) map[string]string {
   147  	if len(targetFields) == 0 {
   148  		return nil
   149  	}
   150  
   151  	tagMap := make(map[string]string, len(logTags))
   152  	for _, logTag := range logTags {
   153  		var tagName string
   154  		if strings.HasPrefix(logTag.Key, tagPrefix) {
   155  			tagName = targetTagPrefix + logTag.Key[len(tagPrefix):]
   156  		} else {
   157  			tagName = logTag.Key
   158  		}
   159  		tagMap[tagName] = logTag.Value
   160  	}
   161  
   162  	desiredValue := make(map[string]string, len(targetFields))
   163  	for _, field := range targetFields {
   164  		v, ok := tagMap[field]
   165  		if !ok {
   166  			continue
   167  		}
   168  		desiredValue[field] = v
   169  	}
   170  	return desiredValue
   171  }