github.com/alibaba/ilogtail/pkg@v0.0.0-20250526110833-c53b480d046c/protocol/decoder/prometheus/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 prometheus 16 17 import ( 18 "bytes" 19 "fmt" 20 "io" 21 "net/http" 22 "strings" 23 24 "github.com/gogo/protobuf/proto" 25 "github.com/prometheus/common/expfmt" 26 "github.com/prometheus/common/model" 27 "github.com/prometheus/prometheus/prompb" 28 "github.com/richardartoul/molecule" 29 "github.com/richardartoul/molecule/src/codec" 30 31 "github.com/alibaba/ilogtail/pkg/helper" 32 "github.com/alibaba/ilogtail/pkg/models" 33 "github.com/alibaba/ilogtail/pkg/protocol" 34 "github.com/alibaba/ilogtail/pkg/protocol/decoder/common" 35 ) 36 37 const ( 38 metricNameKey = "__name__" 39 labelsKey = "__labels__" 40 timeNanoKey = "__time_nano__" 41 valueKey = "__value__" 42 tagDB = "__tag__:db" 43 metaDBKey = "db" 44 ) 45 46 const ( 47 contentEncodingKey = "Content-Encoding" 48 contentTypeKey = "Content-Type" 49 pbContentType = "application/x-protobuf" 50 snappyEncoding = "snappy" 51 ) 52 53 // field index of the proto message models 54 // ref: https://github.com/prometheus/prometheus/blob/cb045c0e4b94bbf3eee174d91b5ef2b8553948d5/prompb/types.proto#L123 55 const ( 56 promPbFieldIndexTimeSeries = 1 57 promPbFieldIndexLabels = 1 58 promPbFieldIndexSamples = 2 59 promPbFieldIndexLabelName = 1 60 promPbFieldIndexLabelValue = 2 61 promPbFieldIndexSampleValue = 1 62 promPbFieldIndexSampleTimestamp = 2 63 ) 64 65 // Decoder impl 66 type Decoder struct { 67 AllowUnsafeMode bool 68 } 69 70 // Decode impl 71 func (d *Decoder) Decode(data []byte, req *http.Request, tags map[string]string) (logs []*protocol.Log, err error) { 72 if req.Header.Get(contentEncodingKey) == snappyEncoding && 73 strings.HasPrefix(req.Header.Get(contentTypeKey), pbContentType) { 74 return d.decodeInRemoteWriteFormat(data, req) 75 } 76 return d.decodeInExpFmt(data, req) 77 } 78 79 func (d *Decoder) decodeInExpFmt(data []byte, _ *http.Request) (logs []*protocol.Log, err error) { 80 decoder := expfmt.NewDecoder(bytes.NewReader(data), expfmt.FmtText) 81 sampleDecoder := expfmt.SampleDecoder{ 82 Dec: decoder, 83 Opts: &expfmt.DecodeOptions{ 84 Timestamp: model.Now(), 85 }, 86 } 87 88 for { 89 s := &model.Vector{} 90 if err = sampleDecoder.Decode(s); err != nil { 91 if err == io.EOF { 92 err = nil 93 break 94 } 95 break 96 } 97 for _, sample := range *s { 98 var name string 99 var labels helper.MetricLabels 100 labelsSet := (model.LabelSet)(sample.Metric) 101 for k, v := range labelsSet { 102 if k == model.MetricNameLabel { 103 name = string(v) 104 continue 105 } 106 labels.Append(string(k), string(v)) 107 } 108 metricLog := helper.NewMetricLog(name, sample.Timestamp.UnixNano(), float64(sample.Value), &labels) 109 logs = append(logs, metricLog) 110 } 111 } 112 113 return logs, err 114 } 115 116 func (d *Decoder) ParseRequest(res http.ResponseWriter, req *http.Request, maxBodySize int64) (data []byte, statusCode int, err error) { 117 return common.CollectBody(res, req, maxBodySize) 118 } 119 120 func (d *Decoder) decodeInRemoteWriteFormat(data []byte, req *http.Request) (logs []*protocol.Log, err error) { 121 var metrics prompb.WriteRequest 122 err = proto.Unmarshal(data, &metrics) 123 if err != nil { 124 return nil, err 125 } 126 127 db := req.FormValue(metaDBKey) 128 for _, m := range metrics.Timeseries { 129 var metricName string 130 var labels helper.MetricLabels 131 for _, label := range m.Labels { 132 if label.Name == model.MetricNameLabel { 133 metricName = label.Value 134 continue 135 } 136 labels.Append(label.Name, label.Value) 137 } 138 for _, sample := range m.Samples { 139 metricLog := helper.NewMetricLog(metricName, sample.Timestamp, sample.Value, &labels) 140 if len(db) > 0 { 141 metricLog.Contents = append(metricLog.Contents, &protocol.Log_Content{ 142 Key: tagDB, 143 Value: db, 144 }) 145 } 146 logs = append(logs, metricLog) 147 } 148 } 149 150 return logs, nil 151 } 152 153 func (d *Decoder) DecodeV2(data []byte, req *http.Request) (groups []*models.PipelineGroupEvents, err error) { 154 if len(data) == 0 { 155 return nil, nil 156 } 157 158 meta := models.NewMetadata() 159 commonTags := models.NewTags() 160 if db := req.FormValue(metaDBKey); len(db) > 0 { 161 meta.Add(metaDBKey, db) 162 } 163 164 if req.Header.Get(contentEncodingKey) == snappyEncoding && 165 strings.HasPrefix(req.Header.Get(contentTypeKey), pbContentType) { 166 var groupEvents *models.PipelineGroupEvents 167 if d.AllowUnsafeMode { 168 groupEvents, err = ParsePromPbToPipelineGroupEventsUnsafe(data, meta, commonTags) 169 } else { 170 groupEvents, err = ConvertPromRequestToPipelineGroupEvents(data, meta, commonTags) 171 } 172 if err != nil { 173 return nil, err 174 } 175 return []*models.PipelineGroupEvents{groupEvents}, nil 176 } 177 178 return ConvertExpFmtDataToPipelineGroupEvents(data, meta, commonTags) 179 } 180 181 func ConvertExpFmtDataToPipelineGroupEvents(data []byte, metaInfo models.Metadata, commonTags models.Tags) (pg []*models.PipelineGroupEvents, err error) { 182 decoder := expfmt.NewDecoder(bytes.NewReader(data), expfmt.FmtText) 183 sampleDecoder := expfmt.SampleDecoder{ 184 Dec: decoder, 185 Opts: &expfmt.DecodeOptions{ 186 Timestamp: model.Now(), 187 }, 188 } 189 190 for { 191 s := model.Vector{} 192 if err = sampleDecoder.Decode(&s); err != nil { 193 if err == io.EOF { 194 err = nil 195 break 196 } 197 break 198 } 199 groupEvents, err := ConvertVectorToPipelineGroupEvents(&s, metaInfo, commonTags) 200 if err == nil { 201 pg = append(pg, groupEvents) 202 } 203 } 204 205 return 206 } 207 208 func ConvertVectorToPipelineGroupEvents(s *model.Vector, metaInfo models.Metadata, commonTags models.Tags) (*models.PipelineGroupEvents, error) { 209 groupEvent := &models.PipelineGroupEvents{ 210 Group: models.NewGroup(metaInfo, commonTags), 211 } 212 213 for _, sample := range *s { 214 metricName, tags := parseModelMetric(sample.Metric) 215 metric := models.NewSingleValueMetric( 216 metricName, 217 models.MetricTypeGauge, 218 tags, 219 sample.Timestamp.UnixNano(), 220 sample.Value, 221 ) 222 groupEvent.Events = append(groupEvent.Events, metric) 223 } 224 225 return groupEvent, nil 226 } 227 228 func parseModelMetric(metric model.Metric) (string, models.Tags) { 229 var metricName string 230 tags := models.NewTags() 231 labels := (model.LabelSet)(metric) 232 233 for k, v := range labels { 234 if k == model.MetricNameLabel { 235 metricName = string(v) 236 continue 237 } 238 tags.Add(string(k), string(v)) 239 } 240 241 return metricName, tags 242 } 243 244 func ConvertPromRequestToPipelineGroupEvents(data []byte, metaInfo models.Metadata, commonTags models.Tags) (*models.PipelineGroupEvents, error) { 245 var promRequest prompb.WriteRequest 246 err := proto.Unmarshal(data, &promRequest) 247 if err != nil { 248 return nil, err 249 } 250 251 groupEvent := &models.PipelineGroupEvents{ 252 Group: models.NewGroup(metaInfo, commonTags), 253 } 254 255 for _, ts := range promRequest.Timeseries { 256 var metricName string 257 tags := models.NewTags() 258 for _, label := range ts.Labels { 259 if label.Name == metricNameKey { 260 metricName = label.Value 261 continue 262 } 263 tags.Add(label.Name, label.Value) 264 } 265 266 for _, dataPoint := range ts.Samples { 267 metric := models.NewSingleValueMetric( 268 metricName, 269 models.MetricTypeGauge, 270 tags, 271 model.Time(dataPoint.GetTimestamp()).Time().UnixNano(), 272 dataPoint.GetValue(), 273 ) 274 groupEvent.Events = append(groupEvent.Events, metric) 275 } 276 } 277 278 return groupEvent, nil 279 } 280 281 func ParsePromPbToPipelineGroupEventsUnsafe(data []byte, metaInfo models.Metadata, commonTags models.Tags) (*models.PipelineGroupEvents, error) { 282 groupEvent := &models.PipelineGroupEvents{ 283 Group: models.NewGroup(metaInfo, commonTags), 284 } 285 286 buffer := codec.NewBuffer(data) 287 err1 := molecule.MessageEach(buffer, func(fieldNum int32, v molecule.Value) (bool, error) { 288 if fieldNum == promPbFieldIndexTimeSeries { 289 serieBytes, err2 := v.AsBytesUnsafe() 290 if err2 != nil { 291 return false, err2 292 } 293 294 var metricName string 295 metricTags := models.NewTags() 296 297 serieBuffer := codec.NewBuffer(serieBytes) 298 err2 = molecule.MessageEach(serieBuffer, func(fieldNum int32, v molecule.Value) (bool, error) { 299 switch fieldNum { 300 case promPbFieldIndexLabels: // Labels 301 labelBytes, err3 := v.AsBytesUnsafe() 302 if err3 != nil { 303 return false, err3 304 } 305 306 var name, value string 307 308 labelBuffer := codec.NewBuffer(labelBytes) 309 err3 = molecule.MessageEach(labelBuffer, func(fieldNum int32, v molecule.Value) (bool, error) { 310 var err4 error 311 switch fieldNum { 312 case promPbFieldIndexLabelName: // Name 313 name, err4 = v.AsStringUnsafe() 314 if err4 != nil { 315 return false, err4 316 } 317 case promPbFieldIndexLabelValue: // Value 318 value, err4 = v.AsStringUnsafe() 319 if err4 != nil { 320 return false, err4 321 } 322 } 323 return true, nil 324 }) 325 if err3 != nil { 326 return false, err3 327 } 328 329 if name == metricNameKey { 330 metricName = value 331 } else { 332 metricTags.Add(name, value) 333 } 334 case promPbFieldIndexSamples: // Samples 335 sampleBytes, err3 := v.AsBytesUnsafe() 336 if err3 != nil { 337 return false, err3 338 } 339 340 var value float64 341 var timestamp int64 342 343 sampleBuffer := codec.NewBuffer(sampleBytes) 344 err3 = molecule.MessageEach(sampleBuffer, func(fieldNum int32, v molecule.Value) (bool, error) { 345 var err4 error 346 switch fieldNum { 347 case promPbFieldIndexSampleValue: // Value 348 value, err4 = v.AsDouble() 349 if err4 != nil { 350 return false, err4 351 } 352 case promPbFieldIndexSampleTimestamp: // Timestamp 353 timestamp, err4 = v.AsInt64() 354 if err4 != nil { 355 return false, err4 356 } 357 } 358 return true, nil 359 }) 360 if err3 != nil { 361 return false, err3 362 } 363 364 if metricName == "" { 365 return false, fmt.Errorf("fields in mix order") 366 } 367 368 metric := models.NewSingleValueMetric( 369 metricName, 370 models.MetricTypeGauge, 371 metricTags, 372 timestamp*1e6, 373 value, 374 ) 375 groupEvent.Events = append(groupEvent.Events, metric) 376 } 377 return true, nil 378 }) 379 if err2 != nil { 380 return false, err2 381 } 382 } 383 return true, nil 384 }) 385 if err1 != nil { 386 return nil, err1 387 } 388 return groupEvent, nil 389 }