Skip to content

Commit

Permalink
[receiver/prometheusremotewrite] validate if scope is already present (
Browse files Browse the repository at this point in the history
…#36927)

<!--Ex. Fixing a bug - Describe the bug and how this fixes the issue.
Ex. Adding a feature - Explain what this achieves.-->
#### Description

Continuing the work done in [this
PR](#35656),
addressing a TODO.

---------

Co-authored-by: Arthur Silva Sens <arthursens2005@gmail.com>
  • Loading branch information
perebaj and ArthurSens authored Jan 16, 2025
1 parent cab9457 commit 814307e
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 5 deletions.
27 changes: 27 additions & 0 deletions .chloggen/prwreceiver-checkscope.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Use this changelog template to create an entry for release notes.

# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
change_type: enhancement

# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
component: receiver/prometheusremotewrite

# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
note: Check if Scope is already present comparing with the received labels

# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
issues: [36927]

# (Optional) One or more lines of additional information to render under the primary note.
# These lines will be padded with 2 spaces and then inserted directly into the document.
# Use pipe (|) for multiline entries.
subtext:

# If your change doesn't affect end users or the exported elements of any package,
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.
# Optional: The change log or logs in which this entry should be included.
# e.g. '[user]' or '[user, api]'
# Include 'user' if the change is relevant to end users.
# Include 'api' if there is a change to a library API.
# Default: '[user]'
change_logs: [user]
23 changes: 20 additions & 3 deletions receiver/prometheusremotewritereceiver/receiver.go
Original file line number Diff line number Diff line change
Expand Up @@ -234,9 +234,26 @@ func addGaugeDatapoints(rm pmetric.ResourceMetrics, ls labels.Labels, ts writev2
// In OTel name+type+unit is the unique identifier of a metric and we should not create
// a new metric if it already exists.

// TODO: Check if Scope is already present by comparing labels "otel_scope_name" and "otel_scope_version"
// with Scope.Name and Scope.Version. If it is present, we should append to the existing Scope.
m := rm.ScopeMetrics().AppendEmpty().Metrics().AppendEmpty().SetEmptyGauge()
scopeName := ls.Get("otel_scope_name")
scopeVersion := ls.Get("otel_scope_version")
// TODO: If the scope version or scope name is empty, get the information from the collector build tags.
// More: https://opentelemetry.io/docs/specs/otel/compatibility/prometheus_and_openmetrics/#:~:text=Metrics%20which%20do%20not%20have%20an%20otel_scope_name%20or%20otel_scope_version%20label%20MUST%20be%20assigned%20an%20instrumentation%20scope%20identifying%20the%20entity%20performing%20the%20translation%20from%20Prometheus%20to%20OpenTelemetry%20(e.g.%20the%20collector%E2%80%99s%20prometheus%20receiver)

// Check if the name and version present in the labels are already present in the ResourceMetrics.
// If it is not present, we should create a new ScopeMetrics.
// Otherwise, we should append to the existing ScopeMetrics.
for j := 0; j < rm.ScopeMetrics().Len(); j++ {
scope := rm.ScopeMetrics().At(j)
if scopeName == scope.Scope().Name() && scopeVersion == scope.Scope().Version() {
addDatapoints(scope.Metrics().AppendEmpty().SetEmptyGauge().DataPoints(), ls, ts)
return
}
}

scope := rm.ScopeMetrics().AppendEmpty()
scope.Scope().SetName(scopeName)
scope.Scope().SetVersion(scopeVersion)
m := scope.Metrics().AppendEmpty().SetEmptyGauge()
addDatapoints(m.DataPoints(), ls, ts)
}

Expand Down
60 changes: 58 additions & 2 deletions receiver/prometheusremotewritereceiver/receiver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,63 @@ func TestTranslateV2(t *testing.T) {
expectedMetrics pmetric.Metrics
expectedStats remote.WriteResponseStats
}{
{
name: "duplicated scope name and version",
request: &writev2.Request{
Symbols: []string{
"",
"__name__", "test_metric",
"job", "service-x/test",
"instance", "107cn001",
"otel_scope_name", "scope1",
"otel_scope_version", "v1",
"otel_scope_name", "scope2",
"otel_scope_version", "v2",
"d", "e",
"foo", "bar",
},
Timeseries: []writev2.TimeSeries{
{
Metadata: writev2.Metadata{Type: writev2.Metadata_METRIC_TYPE_GAUGE},
LabelsRefs: []uint32{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 16}, // Same scope: scope_name: scope1. scope_version v1
Samples: []writev2.Sample{{Value: 1, Timestamp: 1}},
},
{
Metadata: writev2.Metadata{Type: writev2.Metadata_METRIC_TYPE_GAUGE},
LabelsRefs: []uint32{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 16}, // Same scope: scope_name: scope1. scope_version v1
Samples: []writev2.Sample{{Value: 2, Timestamp: 2}},
},
{
Metadata: writev2.Metadata{Type: writev2.Metadata_METRIC_TYPE_GAUGE},
LabelsRefs: []uint32{1, 2, 3, 4, 5, 6, 11, 12, 13, 14, 17, 18}, // Different scope: scope_name: scope2. scope_version v2
Samples: []writev2.Sample{{Value: 3, Timestamp: 3}},
},
},
},
expectedMetrics: func() pmetric.Metrics {
expected := pmetric.NewMetrics()
rm1 := expected.ResourceMetrics().AppendEmpty()
rmAttributes1 := rm1.Resource().Attributes()
rmAttributes1.PutStr("service.namespace", "service-x")
rmAttributes1.PutStr("service.name", "test")
rmAttributes1.PutStr("service.instance.id", "107cn001")
sm1 := rm1.ScopeMetrics().AppendEmpty()
sm1.Scope().SetName("scope1")
sm1.Scope().SetVersion("v1")
sm1Attributes := sm1.Metrics().AppendEmpty().SetEmptyGauge().DataPoints().AppendEmpty().Attributes()
sm1Attributes.PutStr("d", "e")
sm2Attributes := sm1.Metrics().AppendEmpty().SetEmptyGauge().DataPoints().AppendEmpty().Attributes()
sm2Attributes.PutStr("d", "e")

sm2 := rm1.ScopeMetrics().AppendEmpty()
sm2.Scope().SetName("scope2")
sm2.Scope().SetVersion("v2")
sm3Attributes := sm2.Metrics().AppendEmpty().SetEmptyGauge().DataPoints().AppendEmpty().Attributes()
sm3Attributes.PutStr("foo", "bar")
return expected
}(),
expectedStats: remote.WriteResponseStats{},
},
{
name: "missing metric name",
request: &writev2.Request{
Expand Down Expand Up @@ -185,8 +242,7 @@ func TestTranslateV2(t *testing.T) {
sm1Attributes.PutStr("foo", "bar")
// Since we don't check "scope_name" and "scope_version", we end up with duplicated scope metrics for repeated series.
// TODO: Properly handle scope metrics.
sm2 := rm1.ScopeMetrics().AppendEmpty()
sm2Attributes := sm2.Metrics().AppendEmpty().SetEmptyGauge().DataPoints().AppendEmpty().Attributes()
sm2Attributes := sm1.Metrics().AppendEmpty().SetEmptyGauge().DataPoints().AppendEmpty().Attributes()
sm2Attributes.PutStr("d", "e")
sm2Attributes.PutStr("foo", "bar")

Expand Down

0 comments on commit 814307e

Please sign in to comment.