Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions pkg/translator/prometheusremotewrite/helper_v2.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

package prometheusremotewrite // import "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/prometheusremotewrite"

import (
"github.com/prometheus/common/model"
writev2 "github.com/prometheus/prometheus/prompb/io/prometheus/write/v2"
"go.opentelemetry.io/collector/pdata/pcommon"
conventions "go.opentelemetry.io/otel/semconv/v1.25.0"

prometheustranslator "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/prometheus"
)

// addResourceTargetInfoV2 converts the resource to the target info metric.
func addResourceTargetInfoV2(resource pcommon.Resource, settings Settings, timestamp pcommon.Timestamp, converter *prometheusConverterV2) {
if settings.DisableTargetInfo || timestamp == 0 {
return
}

attributes := resource.Attributes()
identifyingAttrs := []string{
string(conventions.ServiceNamespaceKey),
string(conventions.ServiceNameKey),
string(conventions.ServiceInstanceIDKey),
}
nonIdentifyingAttrsCount := attributes.Len()
for _, a := range identifyingAttrs {
_, haveAttr := attributes.Get(a)
if haveAttr {
nonIdentifyingAttrsCount--
}
}
if nonIdentifyingAttrsCount == 0 {
// If we only have job + instance, then target_info isn't useful, so don't add it.
return
}

name := prometheustranslator.TargetInfoMetricName
if len(settings.Namespace) > 0 {
// TODO what to do with this in case of full utf-8 support?
name = settings.Namespace + "_" + name
}

labels := createAttributes(resource, attributes, settings.ExternalLabels, identifyingAttrs, false, model.MetricNameLabel, name)
haveIdentifier := false
for _, l := range labels {
if l.Name == model.JobLabel || l.Name == model.InstanceLabel {
haveIdentifier = true
break
}
}

if !haveIdentifier {
// We need at least one identifying label to generate target_info.
return
}

sample := &writev2.Sample{
Value: float64(1),
// convert ns to ms
Timestamp: convertTimeStamp(timestamp),
}
converter.addSample(sample, labels, metadata{
Type: writev2.Metadata_METRIC_TYPE_GAUGE,
Help: "Target metadata",
})
}
155 changes: 155 additions & 0 deletions pkg/translator/prometheusremotewrite/helper_v2_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

package prometheusremotewrite

import (
"testing"

"github.com/prometheus/common/model"
"github.com/prometheus/prometheus/prompb"
writev2 "github.com/prometheus/prometheus/prompb/io/prometheus/write/v2"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.opentelemetry.io/collector/pdata/pcommon"
conventions "go.opentelemetry.io/otel/semconv/v1.25.0"

"github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal/testdata"
)

func TestAddResourceTargetInfoV2(t *testing.T) {
resourceAttrMap := map[string]any{
string(conventions.ServiceNameKey): "service-name",
string(conventions.ServiceNamespaceKey): "service-namespace",
string(conventions.ServiceInstanceIDKey): "service-instance-id",
}
resourceWithServiceAttrs := pcommon.NewResource()
require.NoError(t, resourceWithServiceAttrs.Attributes().FromRaw(resourceAttrMap))
resourceWithServiceAttrs.Attributes().PutStr("resource_attr", "resource-attr-val-1")
resourceWithOnlyServiceAttrs := pcommon.NewResource()
require.NoError(t, resourceWithOnlyServiceAttrs.Attributes().FromRaw(resourceAttrMap))
// service.name is an identifying resource attribute.
resourceWithOnlyServiceName := pcommon.NewResource()
resourceWithOnlyServiceName.Attributes().PutStr(string(conventions.ServiceNameKey), "service-name")
resourceWithOnlyServiceName.Attributes().PutStr("resource_attr", "resource-attr-val-1")
// service.instance.id is an identifying resource attribute.
resourceWithOnlyServiceID := pcommon.NewResource()
resourceWithOnlyServiceID.Attributes().PutStr(string(conventions.ServiceInstanceIDKey), "service-instance-id")
resourceWithOnlyServiceID.Attributes().PutStr("resource_attr", "resource-attr-val-1")
for _, tc := range []struct {
desc string
resource pcommon.Resource
settings Settings
timestamp pcommon.Timestamp
wantLabels []prompb.Label
wantLabelsRefs []uint32
wantHelpRef uint32
}{
{
desc: "empty resource",
resource: pcommon.NewResource(),
},
{
desc: "disable target info metric",
resource: resourceWithOnlyServiceName,
settings: Settings{DisableTargetInfo: true},
},
{
desc: "with resource missing both service.name and service.instance.id resource attributes",
resource: testdata.GenerateMetricsNoLibraries().ResourceMetrics().At(0).Resource(),
timestamp: testdata.TestMetricStartTimestamp,
},
{
desc: "with resource including service.instance.id, and missing service.name resource attribute",
resource: resourceWithOnlyServiceID,
timestamp: testdata.TestMetricStartTimestamp,
wantLabels: []prompb.Label{
{Name: model.MetricNameLabel, Value: "target_info"},
{Name: model.InstanceLabel, Value: "service-instance-id"},
{Name: "resource_attr", Value: "resource-attr-val-1"},
},
wantLabelsRefs: []uint32{1, 2, 3, 4, 5, 6},
wantHelpRef: 7,
},
{
desc: "with resource including service.name, and missing service.instance.id resource attribute",
resource: resourceWithOnlyServiceName,
timestamp: testdata.TestMetricStartTimestamp,
wantLabels: []prompb.Label{
{Name: model.MetricNameLabel, Value: "target_info"},
{Name: model.JobLabel, Value: "service-name"},
{Name: "resource_attr", Value: "resource-attr-val-1"},
},
wantLabelsRefs: []uint32{1, 2, 3, 4, 5, 6},
wantHelpRef: 7,
},
{
desc: "with valid resource, with namespace",
resource: resourceWithOnlyServiceName,
timestamp: testdata.TestMetricStartTimestamp,
settings: Settings{Namespace: "foo"},
wantLabels: []prompb.Label{
{Name: model.MetricNameLabel, Value: "foo_target_info"},
{Name: model.JobLabel, Value: "service-name"},
{Name: "resource_attr", Value: "resource-attr-val-1"},
},
wantLabelsRefs: []uint32{1, 2, 3, 4, 5, 6},
wantHelpRef: 7,
},
{
desc: "with resource, with service attributes",
resource: resourceWithServiceAttrs,
timestamp: testdata.TestMetricStartTimestamp,
wantLabels: []prompb.Label{
{Name: model.MetricNameLabel, Value: "target_info"},
{Name: model.InstanceLabel, Value: "service-instance-id"},
{Name: model.JobLabel, Value: "service-namespace/service-name"},
{Name: "resource_attr", Value: "resource-attr-val-1"},
},
wantLabelsRefs: []uint32{1, 2, 3, 4, 5, 6, 7, 8},
wantHelpRef: 9,
},
{
desc: "with resource, with only service attributes",
resource: resourceWithOnlyServiceAttrs,
timestamp: testdata.TestMetricStartTimestamp,
},
{
// If there's no timestamp, target_info shouldn't be generated, since we don't know when the write is from.
desc: "with resource, with service attributes, without timestamp",
resource: resourceWithServiceAttrs,
timestamp: 0,
},
} {
t.Run(tc.desc, func(t *testing.T) {
converter := newPrometheusConverterV2()

addResourceTargetInfoV2(tc.resource, tc.settings, tc.timestamp, converter)

if len(tc.wantLabels) == 0 || tc.settings.DisableTargetInfo {
assert.Empty(t, converter.timeSeries())
return
}

expected := map[uint64]*writev2.TimeSeries{
timeSeriesSignature(tc.wantLabels): {
LabelsRefs: tc.wantLabelsRefs,
Samples: []writev2.Sample{
{
Value: 1,
Timestamp: 1581452772000,
},
},
Metadata: writev2.Metadata{
Type: writev2.Metadata_METRIC_TYPE_GAUGE,
HelpRef: tc.wantHelpRef,
UnitRef: 0,
},
},
}
assert.Exactly(t, expected, converter.unique)
// TODO check when conflicts handling is implemented
// assert.Empty(t, converter.conflicts)
})
}
}
3 changes: 1 addition & 2 deletions pkg/translator/prometheusremotewrite/metrics_to_prw_v2.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,7 @@ func (c *prometheusConverterV2) fromMetrics(md pmetric.Metrics, settings Setting
}
}
}
// TODO implement
// addResourceTargetInfov2(resource, settings, mostRecentTimestamp, c)
addResourceTargetInfoV2(resource, settings, mostRecentTimestamp, c)
}

return
Expand Down