github.com/alibaba/ilogtail/pkg@v0.0.0-20250526110833-c53b480d046c/protocol/decoder/prometheus/decoder_test.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 "fmt" 19 "net/http" 20 "reflect" 21 "testing" 22 23 "github.com/prometheus/common/model" 24 "github.com/prometheus/prometheus/prompb" 25 "github.com/stretchr/testify/assert" 26 27 "github.com/alibaba/ilogtail/pkg/models" 28 ) 29 30 var textFormat = `# HELP http_requests_total The total number of HTTP requests. 31 # TYPE http_requests_total counter 32 http_requests_total{method="post",code="200"} 1027 1395066363000 33 http_requests_total{method="post",code="400"} 3 1395066363000 34 35 # Escaping in label values: 36 msdos_file_access_time_seconds{path="C:\\DIR\\FILE.TXT",error="Cannot find file:\n\"FILE.TXT\""} 1.458255915e9 37 38 # Minimalistic line: 39 metric_without_timestamp_and_labels 12.47 40 41 # A weird metric from before the epoch: 42 something_weird{problem="division by zero"} +Inf -3982045 43 44 # A histogram, which has a pretty complex representation in the text format: 45 # HELP http_request_duration_seconds A histogram of the request duration. 46 # TYPE http_request_duration_seconds histogram 47 http_request_duration_seconds_bucket{le="0.05"} 24054 48 http_request_duration_seconds_bucket{le="0.1"} 33444 49 http_request_duration_seconds_bucket{le="0.2"} 100392 50 http_request_duration_seconds_bucket{le="0.5"} 129389 51 http_request_duration_seconds_bucket{le="1"} 133988 52 http_request_duration_seconds_bucket{le="+Inf"} 144320 53 http_request_duration_seconds_sum 53423 54 http_request_duration_seconds_count 144320 55 56 # Finally a summary, which has a complex representation, too: 57 # HELP telemetry_requests_metrics_latency_microseconds A summary of the response latency. 58 # TYPE telemetry_requests_metrics_latency_microseconds summary 59 telemetry_requests_metrics_latency_microseconds{quantile="0.01"} 3102 60 telemetry_requests_metrics_latency_microseconds{quantile="0.05"} 3272 61 telemetry_requests_metrics_latency_microseconds{quantile="0.5"} 4773 62 telemetry_requests_metrics_latency_microseconds{quantile="0.9"} 9001 63 telemetry_requests_metrics_latency_microseconds{quantile="0.99"} 76656 64 telemetry_requests_metrics_latency_microseconds_sum 1.7560473e+07 65 telemetry_requests_metrics_latency_microseconds_count 2693 66 ` 67 68 func TestNormal(t *testing.T) { 69 decoder := &Decoder{} 70 req, _ := http.NewRequest("GET", "http://localhost", nil) 71 logs, err := decoder.Decode([]byte(textFormat), req, nil) 72 assert.Nil(t, err) 73 assert.Equal(t, len(logs), 20) 74 for _, log := range logs { 75 fmt.Printf("%s \n", log.String()) 76 } 77 } 78 79 func TestDecodeV2(t *testing.T) { 80 decoder := &Decoder{} 81 req, _ := http.NewRequest("GET", "http://localhost", nil) 82 groupeEventsSlice, err := decoder.DecodeV2([]byte(textFormat), req) 83 assert.Nil(t, err) 84 metricCount := 0 85 for _, pg := range groupeEventsSlice { 86 for _, event := range pg.Events { 87 metricCount++ 88 fmt.Printf("%s \n", event.(*models.Metric).String()) 89 } 90 } 91 92 assert.Equal(t, 20, metricCount) 93 } 94 95 func BenchmarkDecodeV2(b *testing.B) { 96 promRequest := &prompb.WriteRequest{ 97 Timeseries: []prompb.TimeSeries{ 98 { 99 Labels: []prompb.Label{ 100 {Name: metricNameKey, Value: "test_metric"}, 101 {Name: "label1", Value: "value1"}}, 102 Samples: []prompb.Sample{ 103 {Timestamp: 1234567890, Value: 1.23}, 104 {Timestamp: 1234567891, Value: 2.34}}}}, 105 } 106 bytes, _ := promRequest.Marshal() 107 108 decoder := &Decoder{} 109 req, _ := http.NewRequest("GET", "http://localhost", nil) 110 req.Header.Add(contentEncodingKey, snappyEncoding) 111 req.Header.Add(contentTypeKey, pbContentType) 112 b.ReportAllocs() 113 for i := 0; i < b.N; i++ { 114 _, err := decoder.DecodeV2(bytes, req) 115 assert.Nil(b, err) 116 } 117 } 118 119 func TestConvertPromRequestToPipelineGroupEvents(t *testing.T) { 120 // Create a sample prometheus write request 121 promRequest := &prompb.WriteRequest{ 122 Timeseries: []prompb.TimeSeries{ 123 { 124 Labels: []prompb.Label{ 125 {Name: metricNameKey, Value: "test_metric"}, 126 {Name: "label1", Value: "value1"}}, 127 Samples: []prompb.Sample{ 128 {Timestamp: 1234567890, Value: 1.23}, 129 {Timestamp: 1234567891, Value: 2.34}}}}, 130 } 131 132 data, err := promRequest.Marshal() 133 assert.Nil(t, err) 134 135 metaInfo := models.NewMetadataWithKeyValues("meta_name", "test_meta_name") 136 commonTags := models.NewTagsWithKeyValues( 137 "common_tag1", "common_value1", 138 "common_tag2", "common_value2") 139 140 groupEvent, err := ConvertPromRequestToPipelineGroupEvents(data, metaInfo, commonTags) 141 assert.NoError(t, err) 142 143 assert.Equal(t, "test_meta_name", groupEvent.Group.Metadata.Get("meta_name")) 144 assert.Equal(t, 2, groupEvent.Group.Tags.Len()) 145 assert.Equal(t, "common_value1", groupEvent.Group.Tags.Get("common_tag1")) 146 assert.Equal(t, "common_value2", groupEvent.Group.Tags.Get("common_tag2")) 147 148 // Assert that the events were created correctly 149 if len(groupEvent.Events) != 2 { 150 t.Errorf("Expected 2 events, but got %d", len(groupEvent.Events)) 151 } 152 if groupEvent.Events[0].GetName() != "test_metric" { 153 t.Errorf("Expected event name to be 'test_metric', but got '%s'", groupEvent.Events[0].GetName()) 154 } 155 156 metric1, ok := groupEvent.Events[0].(*models.Metric) 157 assert.True(t, ok) 158 159 assert.Equal(t, models.MetricTypeGauge, metric1.MetricType) 160 assert.Equal(t, 1, metric1.Tags.Len()) 161 assert.Equal(t, "value1", metric1.Tags.Get("label1")) 162 assert.Equal(t, uint64(1234567890000000), metric1.Timestamp) 163 assert.Equal(t, 1.23, metric1.Value.GetSingleValue()) 164 165 metric2, ok := groupEvent.Events[1].(*models.Metric) 166 assert.True(t, ok) 167 assert.Equal(t, models.MetricTypeGauge, metric2.MetricType) 168 assert.Equal(t, 1, metric2.Tags.Len()) 169 assert.Equal(t, "value1", metric1.Tags.Get("label1")) 170 assert.Equal(t, uint64(1234567891000000), metric2.Timestamp) 171 assert.Equal(t, 2.34, metric2.Value.GetSingleValue()) 172 } 173 174 func TestParsePromPbToPipelineGroupEvents(t *testing.T) { 175 promRequest := &prompb.WriteRequest{ 176 Timeseries: []prompb.TimeSeries{ 177 { 178 Labels: []prompb.Label{ 179 {Name: metricNameKey, Value: "test_metric"}, 180 {Name: "label1", Value: "value1"}}, 181 Samples: []prompb.Sample{ 182 {Timestamp: 1234567890, Value: 1.23}, 183 {Timestamp: 1234567891, Value: 2.34}}, 184 }, 185 { 186 Labels: []prompb.Label{ 187 {Name: metricNameKey, Value: "test_metric"}, 188 {Name: "label2", Value: "value2"}}, 189 Samples: []prompb.Sample{ 190 {Timestamp: 1234567890, Value: 1.23}, 191 {Timestamp: 1234567891, Value: 2.34}}, 192 }, 193 }, 194 } 195 bytes, err := promRequest.Marshal() 196 assert.Nil(t, err) 197 198 group, err := ParsePromPbToPipelineGroupEventsUnsafe(bytes, models.NewMetadata(), models.NewTags()) 199 assert.Nil(t, err) 200 assert.NotNil(t, group) 201 assert.Equal(t, &models.PipelineGroupEvents{ 202 Group: models.NewGroup(models.NewMetadata(), models.NewTags()), 203 Events: []models.PipelineEvent{ 204 models.NewSingleValueMetric("test_metric", models.MetricTypeGauge, models.NewTagsWithKeyValues("label1", "value1"), 1234567890*1e6, 1.23), 205 models.NewSingleValueMetric("test_metric", models.MetricTypeGauge, models.NewTagsWithKeyValues("label1", "value1"), 1234567891*1e6, 2.34), 206 models.NewSingleValueMetric("test_metric", models.MetricTypeGauge, models.NewTagsWithKeyValues("label2", "value2"), 1234567890*1e6, 1.23), 207 models.NewSingleValueMetric("test_metric", models.MetricTypeGauge, models.NewTagsWithKeyValues("label2", "value2"), 1234567891*1e6, 2.34), 208 }, 209 }, group) 210 } 211 212 func TestConvertExpFmtDataToPipelineGroupEvents(t *testing.T) { 213 var data = `# HELP http_requests_total The total number of HTTP requests. 214 # TYPE http_requests_total counter 215 http_requests_total{method="GET",code="200"} 100 1680073018671516463 216 http_requests_total{method="POST",code="200"} 50 1680073018671516463 217 http_requests_total{method="GET",code="404"} 10 1680073018671516463 218 ` 219 220 inputData := []byte(data) 221 metaInfo := models.NewMetadataWithKeyValues("meta_name", "test_meta_name") 222 commonTags := models.NewTagsWithKeyValues( 223 "common_tag1", "common_value1", 224 "common_tag2", "common_value2") 225 226 // Expected output 227 expectedPG := []*models.PipelineGroupEvents{ 228 { 229 Group: &models.GroupInfo{ 230 Metadata: metaInfo, 231 Tags: commonTags, 232 }, 233 Events: []models.PipelineEvent{ 234 models.NewSingleValueMetric( 235 "http_requests_total", 236 models.MetricTypeGauge, 237 models.NewTagsWithKeyValues("method", "GET", "code", "200"), 238 1680073018671516463, 239 100, 240 ), 241 242 models.NewSingleValueMetric( 243 "http_requests_total", 244 models.MetricTypeGauge, 245 models.NewTagsWithKeyValues("method", "POST", "code", "200"), 246 1680073018671516463, 247 50, 248 ), 249 250 models.NewSingleValueMetric( 251 "http_requests_total", 252 models.MetricTypeGauge, 253 models.NewTagsWithKeyValues("method", "GET", "code", "404"), 254 1680073018671516463, 255 10, 256 ), 257 }, 258 }, 259 } 260 261 pg, err := ConvertExpFmtDataToPipelineGroupEvents(inputData, metaInfo, commonTags) 262 assert.NoError(t, err) 263 assert.Equal(t, len(pg), len(expectedPG)) 264 265 for i, expected := range expectedPG { 266 actual := pg[i] 267 268 if !reflect.DeepEqual(expected.Group, actual.Group) { 269 t.Errorf("Expected metadata %v, but got %v", expected.Group, actual.Group) 270 } 271 272 assert.Equal(t, len(expected.Events), len(actual.Events)) 273 274 for j, expectedEvent := range expected.Events { 275 actualEvent := actual.Events[j] 276 assert.Equal(t, expectedEvent.GetName(), actualEvent.GetName()) 277 expectedMetric, ok := expectedEvent.(*models.Metric) 278 assert.True(t, ok) 279 actualMetric, ok := actualEvent.(*models.Metric) 280 assert.True(t, ok) 281 assert.Equal(t, expectedMetric.Value.GetSingleValue(), actualMetric.Value.GetSingleValue()) 282 assert.Equal(t, expectedMetric.Tags, actualMetric.Tags) 283 } 284 285 } 286 } 287 288 func TestConvertVectorToPipelineGroupEvents(t *testing.T) { 289 // Create a sample vector with three metrics 290 vector := &model.Vector{ 291 &model.Sample{ 292 Metric: model.Metric{model.MetricNameLabel: "http_requests_total", "method": "GET", "code": "200"}, 293 Timestamp: model.Time(1680073018671516463), 294 Value: 100, 295 }, 296 &model.Sample{ 297 Metric: model.Metric{model.MetricNameLabel: "http_requests_total", "method": "POST", "code": "200"}, 298 Timestamp: model.Time(1680073018671516463), 299 Value: 50, 300 }, 301 &model.Sample{ 302 Metric: model.Metric{model.MetricNameLabel: "http_requests_total", "method": "GET", "code": "404"}, 303 Timestamp: model.Time(1680073018671516463), 304 Value: 10, 305 }, 306 } 307 308 // Create some sample metadata and tags 309 metaInfo := models.NewMetadataWithKeyValues("meta_name", "test_meta_name") 310 commonTags := models.NewTagsWithKeyValues( 311 "common_tag1", "common_value1", 312 "common_tag2", "common_value2") 313 314 // Expected output 315 expectedPG := []*models.PipelineGroupEvents{ 316 { 317 Group: &models.GroupInfo{ 318 Metadata: metaInfo, 319 Tags: commonTags, 320 }, 321 Events: []models.PipelineEvent{ 322 models.NewSingleValueMetric( 323 "http_requests_total", 324 models.MetricTypeGauge, 325 models.NewTagsWithKeyValues("method", "GET", "code", "200"), 326 1680073018671516463, 327 100, 328 ), 329 330 models.NewSingleValueMetric( 331 "http_requests_total", 332 models.MetricTypeGauge, 333 models.NewTagsWithKeyValues("method", "POST", "code", "200"), 334 1680073018671516463, 335 50, 336 ), 337 338 models.NewSingleValueMetric( 339 "http_requests_total", 340 models.MetricTypeGauge, 341 models.NewTagsWithKeyValues("method", "GET", "code", "404"), 342 1680073018671516463, 343 10, 344 ), 345 }, 346 }, 347 } 348 349 g, err := ConvertVectorToPipelineGroupEvents(vector, metaInfo, commonTags) 350 pg := []*models.PipelineGroupEvents{g} 351 assert.NoError(t, err) 352 assert.Equal(t, len(pg), len(expectedPG)) 353 354 for i, expected := range expectedPG { 355 actual := pg[i] 356 357 if !reflect.DeepEqual(expected.Group, actual.Group) { 358 t.Errorf("Expected metadata %v, but got %v", expected.Group, actual.Group) 359 } 360 361 assert.Equal(t, len(expected.Events), len(actual.Events)) 362 363 for j, expectedEvent := range expected.Events { 364 actualEvent := actual.Events[j] 365 assert.Equal(t, expectedEvent.GetName(), actualEvent.GetName()) 366 expectedMetric, ok := expectedEvent.(*models.Metric) 367 assert.True(t, ok) 368 actualMetric, ok := actualEvent.(*models.Metric) 369 assert.True(t, ok) 370 assert.Equal(t, expectedMetric.Value.GetSingleValue(), actualMetric.Value.GetSingleValue()) 371 assert.Equal(t, expectedMetric.Tags, actualMetric.Tags) 372 } 373 374 } 375 }