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  }