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 }