mirror of
https://github.com/prometheus/prometheus.git
synced 2025-03-05 20:59:13 -08:00
Merge 1fa34984fa
into 677efa4678
This commit is contained in:
commit
8933c4b2f9
|
@ -110,7 +110,7 @@ func Load(s string, logger *slog.Logger) (*Config, error) {
|
||||||
switch cfg.OTLPConfig.TranslationStrategy {
|
switch cfg.OTLPConfig.TranslationStrategy {
|
||||||
case UnderscoreEscapingWithSuffixes:
|
case UnderscoreEscapingWithSuffixes:
|
||||||
case "":
|
case "":
|
||||||
case NoUTF8EscapingWithSuffixes:
|
case NoTranslation, NoUTF8EscapingWithSuffixes:
|
||||||
if cfg.GlobalConfig.MetricNameValidationScheme == LegacyValidationConfig {
|
if cfg.GlobalConfig.MetricNameValidationScheme == LegacyValidationConfig {
|
||||||
return nil, errors.New("OTLP translation strategy NoUTF8EscapingWithSuffixes is not allowed when UTF8 is disabled")
|
return nil, errors.New("OTLP translation strategy NoUTF8EscapingWithSuffixes is not allowed when UTF8 is disabled")
|
||||||
}
|
}
|
||||||
|
@ -1436,6 +1436,15 @@ var (
|
||||||
// and label name characters that are not alphanumerics/underscores to underscores.
|
// and label name characters that are not alphanumerics/underscores to underscores.
|
||||||
// Unit and type suffixes may be appended to metric names, according to certain rules.
|
// Unit and type suffixes may be appended to metric names, according to certain rules.
|
||||||
UnderscoreEscapingWithSuffixes translationStrategyOption = "UnderscoreEscapingWithSuffixes"
|
UnderscoreEscapingWithSuffixes translationStrategyOption = "UnderscoreEscapingWithSuffixes"
|
||||||
|
// NoTranslation (EXPERIMENTAL): disables all translation of incoming metric
|
||||||
|
// and label names. Note that because metrics in Open Telemetry are considered
|
||||||
|
// distinct if they share the same name but have different Type or Units, for
|
||||||
|
// instance "foo.bar" with units Seconds is a separate series from "foo.bar"
|
||||||
|
// with units Milliseconds. Because prometheus does not yet support type and
|
||||||
|
// unit metadata, these two series would be conflated in Prometheus.
|
||||||
|
// Therefore this setting is experimental and should not be used in
|
||||||
|
// production systems.
|
||||||
|
NoTranslation translationStrategyOption = "NoTranslation"
|
||||||
)
|
)
|
||||||
|
|
||||||
// OTLPConfig is the configuration for writing to the OTLP endpoint.
|
// OTLPConfig is the configuration for writing to the OTLP endpoint.
|
||||||
|
|
|
@ -1590,6 +1590,26 @@ func TestOTLPAllowUTF8(t *testing.T) {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("good config, no translation", func(t *testing.T) {
|
||||||
|
fpath := filepath.Join("testdata", "otlp_no_translation.good.yml")
|
||||||
|
verify := func(t *testing.T, conf *Config, err error) {
|
||||||
|
t.Helper()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, NoTranslation, conf.OTLPConfig.TranslationStrategy)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("LoadFile", func(t *testing.T) {
|
||||||
|
conf, err := LoadFile(fpath, false, promslog.NewNopLogger())
|
||||||
|
verify(t, conf, err)
|
||||||
|
})
|
||||||
|
t.Run("Load", func(t *testing.T) {
|
||||||
|
content, err := os.ReadFile(fpath)
|
||||||
|
require.NoError(t, err)
|
||||||
|
conf, err := Load(string(content), promslog.NewNopLogger())
|
||||||
|
verify(t, conf, err)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("incompatible config", func(t *testing.T) {
|
t.Run("incompatible config", func(t *testing.T) {
|
||||||
fpath := filepath.Join("testdata", "otlp_allow_utf8.incompatible.yml")
|
fpath := filepath.Join("testdata", "otlp_allow_utf8.incompatible.yml")
|
||||||
verify := func(t *testing.T, err error) {
|
verify := func(t *testing.T, err error) {
|
||||||
|
|
2
config/testdata/otlp_no_translation.good.yml
vendored
Normal file
2
config/testdata/otlp_no_translation.good.yml
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
otlp:
|
||||||
|
translation_strategy: NoTranslation
|
|
@ -577,8 +577,8 @@ func (rw *rwExporter) ConsumeMetrics(ctx context.Context, md pmetric.Metrics) er
|
||||||
|
|
||||||
converter := otlptranslator.NewPrometheusConverter()
|
converter := otlptranslator.NewPrometheusConverter()
|
||||||
annots, err := converter.FromMetrics(ctx, md, otlptranslator.Settings{
|
annots, err := converter.FromMetrics(ctx, md, otlptranslator.Settings{
|
||||||
AddMetricSuffixes: true,
|
AddMetricSuffixes: otlpCfg.TranslationStrategy != config.NoTranslation,
|
||||||
AllowUTF8: otlpCfg.TranslationStrategy == config.NoUTF8EscapingWithSuffixes,
|
AllowUTF8: otlpCfg.TranslationStrategy != config.UnderscoreEscapingWithSuffixes,
|
||||||
PromoteResourceAttributes: otlpCfg.PromoteResourceAttributes,
|
PromoteResourceAttributes: otlpCfg.PromoteResourceAttributes,
|
||||||
KeepIdentifyingResourceAttributes: otlpCfg.KeepIdentifyingResourceAttributes,
|
KeepIdentifyingResourceAttributes: otlpCfg.KeepIdentifyingResourceAttributes,
|
||||||
})
|
})
|
||||||
|
|
|
@ -844,6 +844,19 @@ func requireEqual(t *testing.T, expected, actual interface{}, msgAndArgs ...inte
|
||||||
msgAndArgs...)
|
msgAndArgs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func requireContainsSample(t *testing.T, actual []mockSample, expected mockSample) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
for _, got := range actual {
|
||||||
|
if labels.Equal(expected.l, got.l) && expected.t == got.t && expected.v == got.v {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
require.Fail(t, fmt.Sprintf("Sample not found: \n"+
|
||||||
|
"expected: %v\n"+
|
||||||
|
"actual : %v", expected, actual))
|
||||||
|
}
|
||||||
|
|
||||||
func (m *mockAppendable) Appender(_ context.Context) storage.Appender {
|
func (m *mockAppendable) Appender(_ context.Context) storage.Appender {
|
||||||
if m.latestSample == nil {
|
if m.latestSample == nil {
|
||||||
m.latestSample = map[uint64]int64{}
|
m.latestSample = map[uint64]int64{}
|
||||||
|
|
|
@ -382,7 +382,44 @@ func TestWriteStorageApplyConfig_PartialUpdate(t *testing.T) {
|
||||||
|
|
||||||
func TestOTLPWriteHandler(t *testing.T) {
|
func TestOTLPWriteHandler(t *testing.T) {
|
||||||
exportRequest := generateOTLPWriteRequest()
|
exportRequest := generateOTLPWriteRequest()
|
||||||
|
resp, appendable := handleOtlp(t, exportRequest)
|
||||||
|
require.Equal(t, http.StatusOK, resp.StatusCode)
|
||||||
|
|
||||||
|
require.Len(t, appendable.samples, 12) // 1 (counter) + 1 (gauge) + 1 (target_info) + 7 (hist_bucket) + 2 (hist_sum, hist_count)
|
||||||
|
require.Len(t, appendable.histograms, 1) // 1 (exponential histogram)
|
||||||
|
require.Len(t, appendable.exemplars, 1) // 1 (exemplar)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOTLPWriteHandlerNoTranslation(t *testing.T) {
|
||||||
|
timestamp := time.Now()
|
||||||
|
exportRequest := generateCounterOTLPWriteRequest(timestamp)
|
||||||
|
resp, appendable := handleOtlp(t, exportRequest)
|
||||||
|
require.Equal(t, http.StatusOK, resp.StatusCode)
|
||||||
|
|
||||||
|
requireContainsSample(t, appendable.samples, mockSample{
|
||||||
|
l: labels.New(
|
||||||
|
labels.Label{Name: "__name__", Value: "test.counter"},
|
||||||
|
labels.Label{Name: "foo.bar", Value: "baz"},
|
||||||
|
labels.Label{Name: "instance", Value: "test-instance"},
|
||||||
|
labels.Label{Name: "job", Value: "test-service"},
|
||||||
|
),
|
||||||
|
t: timestamp.UnixMilli(),
|
||||||
|
v: 10,
|
||||||
|
})
|
||||||
|
|
||||||
|
requireContainsSample(t, appendable.samples, mockSample{
|
||||||
|
l: labels.New(
|
||||||
|
labels.Label{Name: "__name__", Value: "target_info"},
|
||||||
|
labels.Label{Name: "host.name", Value: "test-host"},
|
||||||
|
labels.Label{Name: "instance", Value: "test-instance"},
|
||||||
|
labels.Label{Name: "job", Value: "test-service"},
|
||||||
|
),
|
||||||
|
t: timestamp.UnixMilli(),
|
||||||
|
v: 1,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleOtlp(t *testing.T, exportRequest pmetricotlp.ExportRequest) (*http.Response, *mockAppendable) {
|
||||||
buf, err := exportRequest.MarshalProto()
|
buf, err := exportRequest.MarshalProto()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
@ -391,9 +428,11 @@ func TestOTLPWriteHandler(t *testing.T) {
|
||||||
req.Header.Set("Content-Type", "application/x-protobuf")
|
req.Header.Set("Content-Type", "application/x-protobuf")
|
||||||
|
|
||||||
appendable := &mockAppendable{}
|
appendable := &mockAppendable{}
|
||||||
|
conf := config.DefaultOTLPConfig
|
||||||
|
conf.TranslationStrategy = config.NoTranslation
|
||||||
handler := NewOTLPWriteHandler(nil, nil, appendable, func() config.Config {
|
handler := NewOTLPWriteHandler(nil, nil, appendable, func() config.Config {
|
||||||
return config.Config{
|
return config.Config{
|
||||||
OTLPConfig: config.DefaultOTLPConfig,
|
OTLPConfig: conf,
|
||||||
}
|
}
|
||||||
}, OTLPOptions{})
|
}, OTLPOptions{})
|
||||||
|
|
||||||
|
@ -401,11 +440,39 @@ func TestOTLPWriteHandler(t *testing.T) {
|
||||||
handler.ServeHTTP(recorder, req)
|
handler.ServeHTTP(recorder, req)
|
||||||
|
|
||||||
resp := recorder.Result()
|
resp := recorder.Result()
|
||||||
require.Equal(t, http.StatusOK, resp.StatusCode)
|
return resp, appendable
|
||||||
|
}
|
||||||
|
|
||||||
require.Len(t, appendable.samples, 12) // 1 (counter) + 1 (gauge) + 1 (target_info) + 7 (hist_bucket) + 2 (hist_sum, hist_count)
|
func generateCounterOTLPWriteRequest(timestamp time.Time) pmetricotlp.ExportRequest {
|
||||||
require.Len(t, appendable.histograms, 1) // 1 (exponential histogram)
|
d := pmetric.NewMetrics()
|
||||||
require.Len(t, appendable.exemplars, 1) // 1 (exemplar)
|
|
||||||
|
resourceMetric := d.ResourceMetrics().AppendEmpty()
|
||||||
|
resourceMetric.Resource().Attributes().PutStr("service.name", "test-service")
|
||||||
|
resourceMetric.Resource().Attributes().PutStr("service.instance.id", "test-instance")
|
||||||
|
resourceMetric.Resource().Attributes().PutStr("host.name", "test-host")
|
||||||
|
|
||||||
|
scopeMetric := resourceMetric.ScopeMetrics().AppendEmpty()
|
||||||
|
|
||||||
|
counterMetric := scopeMetric.Metrics().AppendEmpty()
|
||||||
|
counterMetric.SetName("test.counter")
|
||||||
|
counterMetric.SetDescription("test-counter-description")
|
||||||
|
counterMetric.SetEmptySum()
|
||||||
|
counterMetric.Sum().SetAggregationTemporality(pmetric.AggregationTemporalityCumulative)
|
||||||
|
counterMetric.Sum().SetIsMonotonic(true)
|
||||||
|
|
||||||
|
counterDataPoint := counterMetric.Sum().DataPoints().AppendEmpty()
|
||||||
|
counterDataPoint.SetTimestamp(pcommon.NewTimestampFromTime(timestamp))
|
||||||
|
counterDataPoint.SetDoubleValue(10.0)
|
||||||
|
counterDataPoint.Attributes().PutStr("foo.bar", "baz")
|
||||||
|
|
||||||
|
counterExemplar := counterDataPoint.Exemplars().AppendEmpty()
|
||||||
|
|
||||||
|
counterExemplar.SetTimestamp(pcommon.NewTimestampFromTime(timestamp))
|
||||||
|
counterExemplar.SetDoubleValue(10.0)
|
||||||
|
counterExemplar.SetSpanID(pcommon.SpanID{0, 1, 2, 3, 4, 5, 6, 7})
|
||||||
|
counterExemplar.SetTraceID(pcommon.TraceID{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15})
|
||||||
|
|
||||||
|
return pmetricotlp.NewExportRequestFromMetrics(d)
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateOTLPWriteRequest() pmetricotlp.ExportRequest {
|
func generateOTLPWriteRequest() pmetricotlp.ExportRequest {
|
||||||
|
@ -426,7 +493,7 @@ func generateOTLPWriteRequest() pmetricotlp.ExportRequest {
|
||||||
|
|
||||||
// Generate One Counter
|
// Generate One Counter
|
||||||
counterMetric := scopeMetric.Metrics().AppendEmpty()
|
counterMetric := scopeMetric.Metrics().AppendEmpty()
|
||||||
counterMetric.SetName("test-counter")
|
counterMetric.SetName("test.counter")
|
||||||
counterMetric.SetDescription("test-counter-description")
|
counterMetric.SetDescription("test-counter-description")
|
||||||
counterMetric.SetEmptySum()
|
counterMetric.SetEmptySum()
|
||||||
counterMetric.Sum().SetAggregationTemporality(pmetric.AggregationTemporalityCumulative)
|
counterMetric.Sum().SetAggregationTemporality(pmetric.AggregationTemporalityCumulative)
|
||||||
|
|
Loading…
Reference in a new issue