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

     1  // Copyright 2021 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 influxdb
    16  
    17  import (
    18  	"fmt"
    19  	"net/http"
    20  	"strconv"
    21  	"time"
    22  
    23  	"github.com/influxdata/influxdb/models"
    24  
    25  	"github.com/alibaba/ilogtail/pkg/helper"
    26  	imodels "github.com/alibaba/ilogtail/pkg/models"
    27  	"github.com/alibaba/ilogtail/pkg/protocol"
    28  	"github.com/alibaba/ilogtail/pkg/protocol/decoder/common"
    29  	"github.com/alibaba/ilogtail/pkg/util"
    30  )
    31  
    32  const (
    33  	typeKey      = "__type__"
    34  	fieldNameKey = "__field__"
    35  )
    36  
    37  const (
    38  	valueTypeFloat  = "float"
    39  	valueTypeInt    = "int"
    40  	valueTypeBool   = "bool"
    41  	valueTypeString = "string"
    42  )
    43  
    44  const tagDB = "__tag__:db"
    45  
    46  const metadataKeyDB = "db"
    47  
    48  const (
    49  	formDataKeyDB        = "db"
    50  	formDataKeyPrecision = "precision"
    51  )
    52  
    53  // Decoder impl
    54  type Decoder struct {
    55  	FieldsExtend bool
    56  }
    57  
    58  func (d *Decoder) Decode(data []byte, req *http.Request, tags map[string]string) (logs []*protocol.Log, decodeErr error) {
    59  	points, err := d.decodeToInfluxdbPoints(data, req)
    60  	if err != nil {
    61  		return nil, err
    62  	}
    63  	logs = d.parsePointsToLogs(points, req)
    64  	return logs, nil
    65  }
    66  
    67  func (d *Decoder) ParseRequest(res http.ResponseWriter, req *http.Request, maxBodySize int64) (data []byte, statusCode int, err error) {
    68  	return common.CollectBody(res, req, maxBodySize)
    69  }
    70  
    71  func (d *Decoder) DecodeV2(data []byte, req *http.Request) ([]*imodels.PipelineGroupEvents, error) {
    72  	points, err := d.decodeToInfluxdbPoints(data, req)
    73  	if err != nil {
    74  		return nil, err
    75  	}
    76  	return d.parsePointsToEvents(points, req)
    77  }
    78  
    79  func (d *Decoder) decodeToInfluxdbPoints(data []byte, req *http.Request) ([]models.Point, error) {
    80  	precision := req.FormValue(formDataKeyPrecision)
    81  	var points []models.Point
    82  	var err error
    83  	if precision != "" {
    84  		points, err = models.ParsePointsWithPrecision(data, time.Now().UTC(), precision)
    85  	} else {
    86  		points, err = models.ParsePoints(data)
    87  	}
    88  	if err != nil {
    89  		return nil, err
    90  	}
    91  	return points, nil
    92  }
    93  
    94  func (d *Decoder) parsePointsToEvents(points []models.Point, req *http.Request) ([]*imodels.PipelineGroupEvents, error) {
    95  	group := &imodels.PipelineGroupEvents{
    96  		Group:  imodels.NewGroup(imodels.NewMetadata(), imodels.NewTags()),
    97  		Events: make([]imodels.PipelineEvent, 0, len(points)),
    98  	}
    99  	if db := req.FormValue(formDataKeyDB); len(db) > 0 {
   100  		group.Group.Metadata.Add(metadataKeyDB, db)
   101  	}
   102  	for _, point := range points {
   103  		tags := imodels.NewTags()
   104  		point.ForEachTag(func(k, v []byte) bool {
   105  			tags.Add(string(k), string(v)) // deep copy here
   106  			return true
   107  		})
   108  		multiValue, typedValue, err := d.parsePointFieldsToMetricValues(point)
   109  		if err != nil {
   110  			return nil, err
   111  		}
   112  		metric := imodels.NewMetric(string(point.Name()), imodels.MetricTypeUntyped, tags, point.UnixNano(), multiValue, typedValue)
   113  		group.Events = append(group.Events, metric)
   114  	}
   115  	return []*imodels.PipelineGroupEvents{group}, nil
   116  }
   117  
   118  func (d *Decoder) parsePointFieldsToMetricValues(p models.Point) (imodels.MetricValue, imodels.MetricTypedValues, error) {
   119  	multiValue := imodels.NewMetricMultiValue()
   120  	typedValue := imodels.NewMetricTypedValues()
   121  
   122  	iter := p.FieldIterator()
   123  	for iter.Next() {
   124  		if len(iter.FieldKey()) == 0 {
   125  			continue
   126  		}
   127  		switch iter.Type() {
   128  		case models.Float:
   129  			v, err := iter.FloatValue()
   130  			if err != nil {
   131  				return nil, nil, fmt.Errorf("unable to unmarshal field %s: %s", string(iter.FieldKey()), err)
   132  			}
   133  			multiValue.Add(string(iter.FieldKey()), v)
   134  		case models.Integer:
   135  			v, err := iter.IntegerValue()
   136  			if err != nil {
   137  				return nil, nil, fmt.Errorf("unable to unmarshal field %s: %s", string(iter.FieldKey()), err)
   138  			}
   139  			multiValue.Add(string(iter.FieldKey()), float64(v))
   140  		case models.Unsigned:
   141  			v, err := iter.UnsignedValue()
   142  			if err != nil {
   143  				return nil, nil, fmt.Errorf("unable to unmarshal field %s: %s", string(iter.FieldKey()), err)
   144  			}
   145  			multiValue.Add(string(iter.FieldKey()), float64(v))
   146  		case models.String:
   147  			typedValue.Add(string(iter.FieldKey()), &imodels.TypedValue{Type: imodels.ValueTypeString, Value: iter.StringValue()})
   148  		case models.Boolean:
   149  			v, err := iter.BooleanValue()
   150  			if err != nil {
   151  				return nil, nil, fmt.Errorf("unable to unmarshal field %s: %s", string(iter.FieldKey()), err)
   152  			}
   153  			typedValue.Add(string(iter.FieldKey()), &imodels.TypedValue{Type: imodels.ValueTypeBoolean, Value: v})
   154  		}
   155  	}
   156  	return multiValue, typedValue, nil
   157  }
   158  
   159  func (d *Decoder) parsePointsToLogs(points []models.Point, req *http.Request) []*protocol.Log {
   160  	db := req.FormValue("db")
   161  	logs := make([]*protocol.Log, 0, len(points))
   162  	for _, s := range points {
   163  		fields, err := s.Fields()
   164  		if err != nil {
   165  			continue
   166  		}
   167  		var valueType string
   168  		var value string
   169  		for field, v := range fields {
   170  			switch v := v.(type) {
   171  			case float64:
   172  				value = strconv.FormatFloat(v, 'g', -1, 64)
   173  				valueType = valueTypeFloat
   174  			case int64:
   175  				value = strconv.FormatInt(v, 10)
   176  				valueType = valueTypeInt
   177  			case bool:
   178  				if v {
   179  					value = "1"
   180  				} else {
   181  					value = "0"
   182  				}
   183  				valueType = valueTypeBool
   184  			case string:
   185  				if !d.FieldsExtend {
   186  					continue
   187  				}
   188  				value = v
   189  				valueType = valueTypeString
   190  			default:
   191  				continue
   192  			}
   193  
   194  			var name string
   195  			if field == "value" {
   196  				name = string(s.Name())
   197  			} else {
   198  				name = string(s.Name()) + ":" + field
   199  			}
   200  			var labels helper.MetricLabels
   201  			for _, v := range s.Tags() {
   202  				labels.Append(util.ZeroCopyBytesToString(v.Key), util.ZeroCopyBytesToString(v.Value))
   203  			}
   204  
   205  			metricLog := helper.NewMetricLogStringVal(name, s.UnixNano(), value, &labels)
   206  			if d.FieldsExtend {
   207  				metricLog.Contents = append(metricLog.Contents,
   208  					&protocol.Log_Content{Key: typeKey, Value: valueType},
   209  					&protocol.Log_Content{Key: fieldNameKey, Value: field})
   210  				if len(db) > 0 {
   211  					metricLog.Contents = append(metricLog.Contents, &protocol.Log_Content{
   212  						Key:   tagDB,
   213  						Value: db,
   214  					})
   215  
   216  				}
   217  			}
   218  			logs = append(logs, metricLog)
   219  		}
   220  	}
   221  	return logs
   222  }