Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[prometheusremotewriteexporter] Support sending trace id and span id for exemplars #8380

Merged
merged 2 commits into from
Mar 15, 2022
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
- `googlecloudpubsubreceiver` Added implementation of Google Cloud Pubsub receiver. (#8391)
- `googlecloudpubsubexporter` Added implementation of Google Cloud Pubsub exporter. (#8391)
- `coralogixexporter` Allow exporter timeout to be configured (#7957)
- `prometheusremotewriteexporter` support adding trace id and span id attached to exemplars (#8380)

### 🛑 Breaking changes 🛑

Expand Down
33 changes: 29 additions & 4 deletions pkg/translator/prometheusremotewrite/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ const (
// according to the prometheus specification
// https://github.com/OpenObservability/OpenMetrics/blob/main/specification/OpenMetrics.md#exemplars
maxExemplarRunes = 128
// Trace and Span id keys are defined as part of the spec:
// https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification%2Fmetrics%2Fdatamodel.md#exemplars-2
traceIDKey = "trace_id"
spanIDKey = "span_id"
)

type bucketBoundsData struct {
Expand Down Expand Up @@ -357,13 +361,32 @@ func getPromExemplars(pt pdata.HistogramDataPoint) []prompb.Exemplar {

for i := 0; i < pt.Exemplars().Len(); i++ {
exemplar := pt.Exemplars().At(i)
exemplarRunes := 0

promExemplar := &prompb.Exemplar{
Value: exemplar.DoubleVal(),
Timestamp: timestamp.FromTime(exemplar.Timestamp().AsTime()),
}
if !exemplar.TraceID().IsEmpty() {
val := exemplar.TraceID().HexString()
exemplarRunes += utf8.RuneCountInString(traceIDKey) + utf8.RuneCountInString(val)
promLabel := prompb.Label{
Name: traceIDKey,
Value: val,
}
promExemplar.Labels = append(promExemplar.Labels, promLabel)
}
if !exemplar.SpanID().IsEmpty() {
val := exemplar.SpanID().HexString()
exemplarRunes += utf8.RuneCountInString(spanIDKey) + utf8.RuneCountInString(val)
promLabel := prompb.Label{
Name: spanIDKey,
Value: val,
}
promExemplar.Labels = append(promExemplar.Labels, promLabel)
}
var labelsFromAttributes []prompb.Label

exemplarRunes := 0
exemplar.FilteredAttributes().Range(func(key string, value pdata.AttributeValue) bool {
val := value.AsString()
exemplarRunes += utf8.RuneCountInString(key) + utf8.RuneCountInString(val)
Expand All @@ -372,12 +395,14 @@ func getPromExemplars(pt pdata.HistogramDataPoint) []prompb.Exemplar {
Value: val,
}

promExemplar.Labels = append(promExemplar.Labels, promLabel)
labelsFromAttributes = append(labelsFromAttributes, promLabel)

return true
})
if exemplarRunes > maxExemplarRunes {
promExemplar.Labels = nil
if exemplarRunes <= maxExemplarRunes {
// only append filtered attributes if it does not cause exemplar
// labels to exceed the max number of runes
promExemplar.Labels = append(promExemplar.Labels, labelsFromAttributes...)
}

promExemplars = append(promExemplars, *promExemplar)
Expand Down
30 changes: 26 additions & 4 deletions pkg/translator/prometheusremotewrite/helper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -424,18 +424,29 @@ func Test_getPromExemplars(t *testing.T) {
}{
{
"with_exemplars",
getHistogramDataPointWithExemplars(tnow, floatVal1, traceIDKey, traceIDValue1),
getHistogramDataPointWithExemplars(t, tnow, floatVal1, traceIDValue1, spanIDValue1, label11, value11),
[]prompb.Exemplar{
{
Value: floatVal1,
Timestamp: timestamp.FromTime(tnow),
Labels: []prompb.Label{getLabel(traceIDKey, traceIDValue1)},
Labels: []prompb.Label{getLabel(traceIDKey, traceIDValue1), getLabel(spanIDKey, spanIDValue1), getLabel(label11, value11)},
},
},
},
{
"with_exemplars_without_trace_or_span",
getHistogramDataPointWithExemplars(t, tnow, floatVal1, "", "", label11, value11),
[]prompb.Exemplar{
{
Value: floatVal1,
Timestamp: timestamp.FromTime(tnow),
Labels: []prompb.Label{getLabel(label11, value11)},
},
},
},
{
"too_many_runes_drops_labels",
getHistogramDataPointWithExemplars(tnow, floatVal1, keyWith129Runes, ""),
getHistogramDataPointWithExemplars(t, tnow, floatVal1, "", "", keyWith129Runes, ""),
[]prompb.Exemplar{
{
Value: floatVal1,
Expand All @@ -445,7 +456,7 @@ func Test_getPromExemplars(t *testing.T) {
},
{
"runes_at_limit_bytes_over_keeps_labels",
getHistogramDataPointWithExemplars(tnow, floatVal1, keyWith128Runes, ""),
getHistogramDataPointWithExemplars(t, tnow, floatVal1, "", "", keyWith128Runes, ""),
[]prompb.Exemplar{
{
Value: floatVal1,
Expand All @@ -454,6 +465,17 @@ func Test_getPromExemplars(t *testing.T) {
},
},
},
{
"too_many_runes_with_exemplar_drops_attrs_keeps_exemplar",
getHistogramDataPointWithExemplars(t, tnow, floatVal1, traceIDValue1, spanIDValue1, keyWith64Runes, ""),
[]prompb.Exemplar{
{
Value: floatVal1,
Timestamp: timestamp.FromTime(tnow),
Labels: []prompb.Label{getLabel(traceIDKey, traceIDValue1), getLabel(spanIDKey, spanIDValue1)},
},
},
},
{
"without_exemplar",
getHistogramDataPoint(),
Expand Down
26 changes: 23 additions & 3 deletions pkg/translator/prometheusremotewrite/testutils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,14 @@
package prometheusremotewrite

import (
"encoding/hex"
"math"
"strings"
"testing"
"time"

"github.com/prometheus/prometheus/prompb"
"github.com/stretchr/testify/require"
"go.opentelemetry.io/collector/model/pdata"
)

Expand All @@ -45,14 +48,16 @@ var (
value41 = "test_value41"
dirty1 = "%"
dirty2 = "?"
traceIDValue1 = "traceID-value1"
traceIDKey = "trace_id"
traceIDValue1 = "4303853f086f4f8c86cf198b6551df84"
spanIDValue1 = "e5513c32795c41b9"
colliding1 = "test.colliding"
colliding2 = "test/colliding"
collidingSanitized = "test_colliding"
keyWith129Runes = "iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii"
// because of the special characters, this has 132 bytes and 128 runes
keyWith128Runes = "iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii世界"
// 64 + trace id + span id = 129 characters
keyWith64Runes = "iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii"

intVal1 int64 = 1
intVal2 int64 = 2
Expand Down Expand Up @@ -221,14 +226,29 @@ func getTimeSeriesWithSamplesAndExemplars(labels []prompb.Label, samples []promp
}
}

func getHistogramDataPointWithExemplars(time time.Time, value float64, attributeKey string, attributeValue string) *pdata.HistogramDataPoint {
func getHistogramDataPointWithExemplars(t *testing.T, time time.Time, value float64, traceID string, spanID string, attributeKey string, attributeValue string) *pdata.HistogramDataPoint {
h := pdata.NewHistogramDataPoint()

e := h.Exemplars().AppendEmpty()
e.SetDoubleVal(value)
e.SetTimestamp(pdata.NewTimestampFromTime(time))
e.FilteredAttributes().Insert(attributeKey, pdata.NewAttributeValueString(attributeValue))

if traceID != "" {
var traceIDBytes [16]byte
traceIDBytesSlice, err := hex.DecodeString(traceID)
require.NoErrorf(t, err, "error decoding trace id: %v", err)
copy(traceIDBytes[:], traceIDBytesSlice)
e.SetTraceID(pdata.NewTraceID(traceIDBytes))
}
if spanID != "" {
var spanIDBytes [8]byte
spanIDBytesSlice, err := hex.DecodeString(spanID)
require.NoErrorf(t, err, "error decoding span id: %v", err)
copy(spanIDBytes[:], spanIDBytesSlice)
e.SetSpanID(pdata.NewSpanID(spanIDBytes))
}

return &h
}

Expand Down