Skip to content

Commit 1b44bce

Browse files
authored
[chore][exporter/sumologic]: add deduplicateErrors and decomposeHistograms functions (#32316)
**Description:** Adds files from Sumo Logic Distribution for OpenTelemetry which are new in order to simplify review and make simpler to move the whole component. This PR is made to simplify #32315 **Link to tracking Issue:** #31479 **Testing:** Unit Tests, also this code is use by our customers and internally. It is not used by now by the component, but will be when migration will be finished **Documentation:** N/A Signed-off-by: Dominik Rosiek <[email protected]>
1 parent 8dce114 commit 1b44bce

File tree

4 files changed

+428
-0
lines changed

4 files changed

+428
-0
lines changed
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package sumologicexporter // import "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/sumologicexporter"
5+
6+
import "fmt"
7+
8+
// deduplicateErrors replaces duplicate instances of the same error in a slice
9+
// with a single error containing the number of times it occurred added as a suffix.
10+
// For example, three occurrences of "error: 502 Bad Gateway"
11+
// are replaced with a single instance of "error: 502 Bad Gateway (x3)".
12+
func deduplicateErrors(errs []error) []error {
13+
if len(errs) < 2 {
14+
return errs
15+
}
16+
17+
errorsWithCounts := []errorWithCount{}
18+
for _, err := range errs {
19+
found := false
20+
for i := range errorsWithCounts {
21+
if errorsWithCounts[i].err.Error() == err.Error() {
22+
found = true
23+
errorsWithCounts[i].count++
24+
break
25+
}
26+
}
27+
if !found {
28+
errorsWithCounts = append(errorsWithCounts, errorWithCount{
29+
err: err,
30+
count: 1,
31+
})
32+
}
33+
}
34+
35+
var uniqueErrors []error
36+
for _, errorWithCount := range errorsWithCounts {
37+
if errorWithCount.count == 1 {
38+
uniqueErrors = append(uniqueErrors, errorWithCount.err)
39+
} else {
40+
uniqueErrors = append(uniqueErrors, fmt.Errorf("%s (x%d)", errorWithCount.err.Error(), errorWithCount.count))
41+
}
42+
}
43+
return uniqueErrors
44+
}
45+
46+
type errorWithCount struct {
47+
err error
48+
count int
49+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package sumologicexporter // import "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/sumologicexporter"
5+
6+
import (
7+
"errors"
8+
"testing"
9+
10+
"github.com/stretchr/testify/assert"
11+
)
12+
13+
func TestDeduplicateErrors(t *testing.T) {
14+
testCases := []struct {
15+
name string
16+
errs []error
17+
expected []error
18+
}{
19+
{
20+
name: "nil is returned as nil",
21+
errs: nil,
22+
expected: nil,
23+
},
24+
{
25+
name: "single error is returned as-is",
26+
errs: []error{
27+
errors.New("Single error"),
28+
},
29+
expected: []error{
30+
errors.New("Single error"),
31+
},
32+
},
33+
{
34+
name: "duplicates are removed",
35+
errs: []error{
36+
errors.New("failed sending data: 502 Bad Gateway"),
37+
errors.New("failed sending data: 400 Bad Request"),
38+
errors.New("failed sending data: 502 Bad Gateway"),
39+
errors.New("failed sending data: 400 Bad Request"),
40+
errors.New("failed sending data: 400 Bad Request"),
41+
errors.New("failed sending data: 400 Bad Request"),
42+
errors.New("failed sending data: 504 Gateway Timeout"),
43+
errors.New("failed sending data: 502 Bad Gateway"),
44+
},
45+
expected: []error{
46+
errors.New("failed sending data: 502 Bad Gateway (x3)"),
47+
errors.New("failed sending data: 400 Bad Request (x4)"),
48+
errors.New("failed sending data: 504 Gateway Timeout"),
49+
},
50+
},
51+
}
52+
53+
for _, testCase := range testCases {
54+
t.Run(testCase.name, func(t *testing.T) {
55+
assert.Equal(t, testCase.expected, deduplicateErrors(testCase.errs))
56+
})
57+
}
58+
}

exporter/sumologicexporter/otlp.go

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package sumologicexporter // import "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/sumologicexporter"
5+
6+
import (
7+
"math"
8+
9+
"go.opentelemetry.io/collector/pdata/pmetric"
10+
)
11+
12+
// decomposeHistograms decomposes any histograms present in the metric data into individual Sums and Gauges
13+
// This is a noop if no Histograms are present, but otherwise makes a copy of the whole structure
14+
// This exists because Sumo doesn't support OTLP histograms yet, and has the same semantics as the conversion to Prometheus format in prometheus_formatter.go
15+
func decomposeHistograms(md pmetric.Metrics) pmetric.Metrics {
16+
// short circuit and do nothing if no Histograms are present
17+
foundHistogram := false
18+
outer:
19+
for i := 0; i < md.ResourceMetrics().Len(); i++ {
20+
resourceMetric := md.ResourceMetrics().At(i)
21+
for j := 0; j < resourceMetric.ScopeMetrics().Len(); j++ {
22+
scopeMetric := resourceMetric.ScopeMetrics().At(j)
23+
for k := 0; k < scopeMetric.Metrics().Len(); k++ {
24+
foundHistogram = scopeMetric.Metrics().At(k).Type() == pmetric.MetricTypeHistogram
25+
if foundHistogram {
26+
break outer
27+
}
28+
}
29+
}
30+
}
31+
if !foundHistogram {
32+
return md
33+
}
34+
35+
decomposed := pmetric.NewMetrics()
36+
md.CopyTo(decomposed)
37+
38+
for i := 0; i < decomposed.ResourceMetrics().Len(); i++ {
39+
resourceMetric := decomposed.ResourceMetrics().At(i)
40+
for j := 0; j < resourceMetric.ScopeMetrics().Len(); j++ {
41+
metrics := resourceMetric.ScopeMetrics().At(j).Metrics()
42+
for k := 0; k < metrics.Len(); k++ {
43+
metric := metrics.At(k)
44+
if metric.Type() == pmetric.MetricTypeHistogram {
45+
decomposedHistogram := decomposeHistogram(metric)
46+
decomposedHistogram.MoveAndAppendTo(metrics)
47+
}
48+
}
49+
metrics.RemoveIf(func(m pmetric.Metric) bool { return m.Type() == pmetric.MetricTypeHistogram })
50+
}
51+
}
52+
53+
return decomposed
54+
}
55+
56+
// decomposeHistogram decomposes a single Histogram metric into individual metrics for count, bucket and sum
57+
// non-Histograms give an empty slice as output
58+
func decomposeHistogram(metric pmetric.Metric) pmetric.MetricSlice {
59+
output := pmetric.NewMetricSlice()
60+
if metric.Type() != pmetric.MetricTypeHistogram {
61+
return output
62+
}
63+
64+
getHistogramSumMetric(metric).MoveTo(output.AppendEmpty())
65+
getHistogramCountMetric(metric).MoveTo(output.AppendEmpty())
66+
getHistogramBucketsMetric(metric).MoveTo(output.AppendEmpty())
67+
68+
return output
69+
}
70+
71+
func getHistogramBucketsMetric(metric pmetric.Metric) pmetric.Metric {
72+
histogram := metric.Histogram()
73+
74+
bucketsMetric := pmetric.NewMetric()
75+
bucketsMetric.SetName(metric.Name() + "_bucket")
76+
bucketsMetric.SetDescription(metric.Description())
77+
bucketsMetric.SetUnit(metric.Unit())
78+
bucketsMetric.SetEmptyGauge()
79+
bucketsDatapoints := bucketsMetric.Gauge().DataPoints()
80+
81+
for i := 0; i < histogram.DataPoints().Len(); i++ {
82+
histogramDataPoint := histogram.DataPoints().At(i)
83+
histogramBounds := histogramDataPoint.ExplicitBounds()
84+
var cumulative uint64
85+
86+
for j := 0; j < histogramBounds.Len(); j++ {
87+
bucketDataPoint := bucketsDatapoints.AppendEmpty()
88+
bound := histogramBounds.At(j)
89+
histogramDataPoint.Attributes().CopyTo(bucketDataPoint.Attributes())
90+
bucketDataPoint.Attributes().PutDouble(prometheusLeTag, bound)
91+
bucketDataPoint.SetStartTimestamp(histogramDataPoint.StartTimestamp())
92+
bucketDataPoint.SetTimestamp(histogramDataPoint.Timestamp())
93+
cumulative += histogramDataPoint.BucketCounts().At(j)
94+
bucketDataPoint.SetIntValue(int64(cumulative))
95+
}
96+
97+
// need to add one more bucket at +Inf
98+
bucketDataPoint := bucketsDatapoints.AppendEmpty()
99+
histogramDataPoint.Attributes().CopyTo(bucketDataPoint.Attributes())
100+
bucketDataPoint.Attributes().PutDouble(prometheusLeTag, math.Inf(1))
101+
bucketDataPoint.SetStartTimestamp(histogramDataPoint.StartTimestamp())
102+
bucketDataPoint.SetTimestamp(histogramDataPoint.Timestamp())
103+
cumulative += histogramDataPoint.BucketCounts().At(histogramDataPoint.ExplicitBounds().Len())
104+
bucketDataPoint.SetIntValue(int64(cumulative))
105+
}
106+
return bucketsMetric
107+
}
108+
109+
func getHistogramSumMetric(metric pmetric.Metric) pmetric.Metric {
110+
histogram := metric.Histogram()
111+
112+
sumMetric := pmetric.NewMetric()
113+
sumMetric.SetName(metric.Name() + "_sum")
114+
sumMetric.SetDescription(metric.Description())
115+
sumMetric.SetUnit(metric.Unit())
116+
sumMetric.SetEmptyGauge()
117+
sumDataPoints := sumMetric.Gauge().DataPoints()
118+
119+
for i := 0; i < histogram.DataPoints().Len(); i++ {
120+
histogramDataPoint := histogram.DataPoints().At(i)
121+
sumDataPoint := sumDataPoints.AppendEmpty()
122+
histogramDataPoint.Attributes().CopyTo(sumDataPoint.Attributes())
123+
sumDataPoint.SetStartTimestamp(histogramDataPoint.StartTimestamp())
124+
sumDataPoint.SetTimestamp(histogramDataPoint.Timestamp())
125+
sumDataPoint.SetDoubleValue(histogramDataPoint.Sum())
126+
}
127+
return sumMetric
128+
}
129+
130+
func getHistogramCountMetric(metric pmetric.Metric) pmetric.Metric {
131+
histogram := metric.Histogram()
132+
133+
countMetric := pmetric.NewMetric()
134+
countMetric.SetName(metric.Name() + "_count")
135+
countMetric.SetDescription(metric.Description())
136+
countMetric.SetUnit(metric.Unit())
137+
countMetric.SetEmptyGauge()
138+
countDataPoints := countMetric.Gauge().DataPoints()
139+
140+
for i := 0; i < histogram.DataPoints().Len(); i++ {
141+
histogramDataPoint := histogram.DataPoints().At(i)
142+
countDataPoint := countDataPoints.AppendEmpty()
143+
histogramDataPoint.Attributes().CopyTo(countDataPoint.Attributes())
144+
countDataPoint.SetStartTimestamp(histogramDataPoint.StartTimestamp())
145+
countDataPoint.SetTimestamp(histogramDataPoint.Timestamp())
146+
countDataPoint.SetIntValue(int64(histogramDataPoint.Count()))
147+
}
148+
return countMetric
149+
}

0 commit comments

Comments
 (0)