Skip to content

Commit 37f7da5

Browse files
committed
Add ScrapeErrors struct to simplify errors usage
Add ScrapeErrors that contains a slice with errors and that has methods to simplify adding new PartialScrapeErrors and regular generic errors. Use new methods to refactor errors appends in receiver/hostmetricsreceiver. Use ScrapeErrors.Combine() in component/componenterror to not create cycling dependencies in consumererrors package.
1 parent bd70c80 commit 37f7da5

File tree

13 files changed

+312
-223
lines changed

13 files changed

+312
-223
lines changed

component/componenterror/errors.go

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,6 @@ package componenterror
1818

1919
import (
2020
"errors"
21-
"fmt"
22-
"strings"
2321

2422
"go.opentelemetry.io/collector/consumer/consumererror"
2523
)
@@ -37,27 +35,5 @@ var (
3735

3836
// CombineErrors converts a list of errors into one error.
3937
func CombineErrors(errs []error) error {
40-
numErrors := len(errs)
41-
if numErrors == 0 {
42-
// No errors
43-
return nil
44-
}
45-
46-
if numErrors == 1 {
47-
return errs[0]
48-
}
49-
50-
errMsgs := make([]string, 0, numErrors)
51-
permanent := false
52-
for _, err := range errs {
53-
if !permanent && consumererror.IsPermanent(err) {
54-
permanent = true
55-
}
56-
errMsgs = append(errMsgs, err.Error())
57-
}
58-
err := fmt.Errorf("[%s]", strings.Join(errMsgs, "; "))
59-
if permanent {
60-
err = consumererror.Permanent(err)
61-
}
62-
return err
38+
return consumererror.CombineErrors(errs)
6339
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Copyright The OpenTelemetry Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package consumererror
16+
17+
import (
18+
"fmt"
19+
"strings"
20+
)
21+
22+
// CombineErrors converts a list of errors into one error.
23+
func CombineErrors(errs []error) error {
24+
numErrors := len(errs)
25+
if numErrors == 0 {
26+
// No errors
27+
return nil
28+
}
29+
30+
if numErrors == 1 {
31+
return errs[0]
32+
}
33+
34+
errMsgs := make([]string, 0, numErrors)
35+
permanent := false
36+
for _, err := range errs {
37+
if !permanent && IsPermanent(err) {
38+
permanent = true
39+
}
40+
errMsgs = append(errMsgs, err.Error())
41+
}
42+
err := fmt.Errorf("[%s]", strings.Join(errMsgs, "; "))
43+
if permanent {
44+
err = Permanent(err)
45+
}
46+
return err
47+
}

component/componenterror/errors_test.go renamed to consumer/consumererror/combineerrors_test.go

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,22 @@
1-
// Copyright The OpenTelemetry Authors
1+
// Copyright The OpenTelemetry Authors
22
//
33
// Licensed under the Apache License, Version 2.0 (the "License");
44
// you may not use this file except in compliance with the License.
55
// You may obtain a copy of the License at
66
//
7-
// http://www.apache.org/licenses/LICENSE-2.0
7+
// http://www.apache.org/licenses/LICENSE-2.0
88
//
99
// Unless required by applicable law or agreed to in writing, software
1010
// distributed under the License is distributed on an "AS IS" BASIS,
1111
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
package componenterror_test
15+
package consumererror
1616

1717
import (
1818
"fmt"
1919
"testing"
20-
21-
"go.opentelemetry.io/collector/component/componenterror"
22-
"go.opentelemetry.io/collector/consumer/consumererror"
2320
)
2421

2522
func TestCombineErrors(t *testing.T) {
@@ -50,20 +47,20 @@ func TestCombineErrors(t *testing.T) {
5047
errors: []error{
5148
fmt.Errorf("foo"),
5249
fmt.Errorf("bar"),
53-
consumererror.Permanent(fmt.Errorf("permanent"))},
50+
Permanent(fmt.Errorf("permanent"))},
5451
expected: "Permanent error: [foo; bar; Permanent error: permanent]",
5552
},
5653
}
5754

5855
for _, tc := range testCases {
59-
got := componenterror.CombineErrors(tc.errors)
56+
got := CombineErrors(tc.errors)
6057
if (got == nil) != tc.expectNil {
6158
t.Errorf("CombineErrors(%v) == nil? Got: %t. Want: %t", tc.errors, got == nil, tc.expectNil)
6259
}
6360
if got != nil && tc.expected != got.Error() {
6461
t.Errorf("CombineErrors(%v) = %q. Want: %q", tc.errors, got, tc.expected)
6562
}
66-
if tc.expectedPermanent && !consumererror.IsPermanent(got) {
63+
if tc.expectedPermanent && !IsPermanent(got) {
6764
t.Errorf("CombineErrors(%v) = %q. Want: consumererror.permanent", tc.errors, got)
6865
}
6966
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// Copyright The OpenTelemetry Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package consumererror
16+
17+
import (
18+
"fmt"
19+
"strings"
20+
)
21+
22+
// ScrapeErrors contains multiple PartialScrapeErrors and can also contain generic errors.
23+
type ScrapeErrors struct {
24+
errs []error
25+
scrapeErrsCount int
26+
}
27+
28+
// Add adds a PartialScrapeError with the provided failed count and error.
29+
func (s *ScrapeErrors) Add(failed int, err error) {
30+
s.errs = append(s.errs, NewPartialScrapeError(err, failed))
31+
s.scrapeErrsCount++
32+
}
33+
34+
// Addf adds a PartialScrapeError with the provided failed count and arguments to format an error.
35+
func (s *ScrapeErrors) Addf(failed int, format string, a ...interface{}) {
36+
s.errs = append(s.errs, NewPartialScrapeError(fmt.Errorf(format, a...), failed))
37+
s.scrapeErrsCount++
38+
}
39+
40+
// Add adds a regular generic error.
41+
func (s *ScrapeErrors) AddRegular(err error) {
42+
s.errs = append(s.errs, err)
43+
}
44+
45+
// Add adds a regular generic error from the provided format specifier.
46+
func (s *ScrapeErrors) AddRegularf(format string, a ...interface{}) {
47+
s.errs = append(s.errs, fmt.Errorf(format, a...))
48+
}
49+
50+
// Combine converts a slice of errors into one error.
51+
// It will return a PartialScrapeError if at least one error in the slice is a PartialScrapeError.
52+
func (s *ScrapeErrors) Combine() error {
53+
if s.scrapeErrsCount == 0 {
54+
return CombineErrors(s.errs)
55+
}
56+
57+
errMsgs := make([]string, 0, len(s.errs))
58+
failedScrapeCount := 0
59+
for _, err := range s.errs {
60+
if partialError, isPartial := err.(PartialScrapeError); isPartial {
61+
failedScrapeCount += partialError.Failed
62+
}
63+
64+
errMsgs = append(errMsgs, err.Error())
65+
}
66+
67+
var err error
68+
if len(s.errs) == 1 {
69+
err = s.errs[0]
70+
} else {
71+
err = fmt.Errorf("[%s]", strings.Join(errMsgs, "; "))
72+
}
73+
74+
return NewPartialScrapeError(err, failedScrapeCount)
75+
}
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
// Copyright The OpenTelemetry Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package consumererror
16+
17+
import (
18+
"errors"
19+
"fmt"
20+
"testing"
21+
22+
"github.com/stretchr/testify/assert"
23+
)
24+
25+
func TestScrapeErrorsAdd(t *testing.T) {
26+
err1 := errors.New("err 1")
27+
err2 := errors.New("err 2")
28+
expected := []error{
29+
PartialScrapeError{error: err1, Failed: 1},
30+
PartialScrapeError{error: err2, Failed: 10},
31+
}
32+
33+
var errs ScrapeErrors
34+
errs.Add(1, err1)
35+
errs.Add(10, err2)
36+
assert.Equal(t, expected, errs.errs)
37+
}
38+
39+
func TestScrapeErrorsAddf(t *testing.T) {
40+
err1 := errors.New("err 10")
41+
err2 := errors.New("err 20")
42+
expected := []error{
43+
PartialScrapeError{error: fmt.Errorf("err: %s", err1), Failed: 20},
44+
PartialScrapeError{error: fmt.Errorf("err %s: %w", "2", err2), Failed: 2},
45+
}
46+
47+
var errs ScrapeErrors
48+
errs.Addf(20, "err: %s", err1)
49+
errs.Addf(2, "err %s: %w", "2", err2)
50+
assert.Equal(t, expected, errs.errs)
51+
}
52+
53+
func TestScrapeErrorsAddRegular(t *testing.T) {
54+
err1 := errors.New("err a")
55+
err2 := errors.New("err b")
56+
expected := []error{err1, err2}
57+
58+
var errs ScrapeErrors
59+
errs.AddRegular(err1)
60+
errs.AddRegular(err2)
61+
assert.Equal(t, expected, errs.errs)
62+
}
63+
64+
func TestScrapeErrorsAddRegularf(t *testing.T) {
65+
err1 := errors.New("err aa")
66+
err2 := errors.New("err bb")
67+
expected := []error{
68+
fmt.Errorf("err: %s", err1),
69+
fmt.Errorf("err %s: %w", "bb", err2),
70+
}
71+
72+
var errs ScrapeErrors
73+
errs.AddRegularf("err: %s", err1)
74+
errs.AddRegularf("err %s: %w", "bb", err2)
75+
assert.Equal(t, expected, errs.errs)
76+
}
77+
78+
func TestScrapeErrorsCombine(t *testing.T) {
79+
testCases := []struct {
80+
errs func() ScrapeErrors
81+
expectedErr string
82+
expectedFailedCount int
83+
expectNil bool
84+
expectedScrape bool
85+
}{
86+
{
87+
errs: func() ScrapeErrors {
88+
var errs ScrapeErrors
89+
return errs
90+
},
91+
expectNil: true,
92+
},
93+
{
94+
errs: func() ScrapeErrors {
95+
var errs ScrapeErrors
96+
errs.Add(10, errors.New("bad scrapes"))
97+
errs.Addf(1, "err: %s", errors.New("bad scrape"))
98+
return errs
99+
},
100+
expectedErr: "[bad scrapes; err: bad scrape]",
101+
expectedFailedCount: 11,
102+
expectedScrape: true,
103+
},
104+
{
105+
errs: func() ScrapeErrors {
106+
var errs ScrapeErrors
107+
errs.AddRegular(errors.New("bad regular"))
108+
errs.AddRegularf("err: %s", errors.New("bad reg"))
109+
return errs
110+
},
111+
expectedErr: "[bad regular; err: bad reg]",
112+
},
113+
{
114+
errs: func() ScrapeErrors {
115+
var errs ScrapeErrors
116+
errs.Add(2, errors.New("bad two scrapes"))
117+
errs.Addf(10, "%d scrapes failed: %s", 10, errors.New("bad things happened"))
118+
errs.AddRegular(errors.New("bad event"))
119+
errs.AddRegularf("event: %s", errors.New("something happened"))
120+
return errs
121+
},
122+
expectedErr: "[bad two scrapes; 10 scrapes failed: bad things happened; bad event; event: something happened]",
123+
expectedFailedCount: 12,
124+
expectedScrape: true,
125+
},
126+
}
127+
128+
for _, tc := range testCases {
129+
scrapeErrs := tc.errs()
130+
if (scrapeErrs.Combine() == nil) != tc.expectNil {
131+
t.Errorf("%+v.Combine() == nil? Got: %t. Want: %t", scrapeErrs, scrapeErrs.Combine() == nil, tc.expectNil)
132+
}
133+
if scrapeErrs.Combine() != nil && tc.expectedErr != scrapeErrs.Combine().Error() {
134+
t.Errorf("%+v.Combine() = %q. Want: %q", scrapeErrs, scrapeErrs.Combine(), tc.expectedErr)
135+
}
136+
if tc.expectedScrape {
137+
partialScrapeErr, ok := scrapeErrs.Combine().(PartialScrapeError)
138+
if !ok {
139+
t.Errorf("%+v.Combine() = %q. Want: PartialScrapeError", scrapeErrs, scrapeErrs.Combine())
140+
} else if tc.expectedFailedCount != partialScrapeErr.Failed {
141+
t.Errorf("%+v.Combine().Failed. Got %d Failed count. Want: %d", scrapeErrs, partialScrapeErr.Failed, tc.expectedFailedCount)
142+
}
143+
}
144+
}
145+
}

receiver/hostmetricsreceiver/internal/scraper/filesystemscraper/filesystem_scraper.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ import (
2525
"go.opentelemetry.io/collector/consumer/pdata"
2626
"go.opentelemetry.io/collector/receiver/hostmetricsreceiver/internal"
2727
"go.opentelemetry.io/collector/receiver/hostmetricsreceiver/internal/metadata"
28-
"go.opentelemetry.io/collector/receiver/scraperhelper"
2928
)
3029

3130
const (
@@ -71,15 +70,15 @@ func (s *scraper) Scrape(_ context.Context) (pdata.MetricSlice, error) {
7170
return metrics, consumererror.NewPartialScrapeError(err, metricsLen)
7271
}
7372

74-
var errors []error
73+
var errors consumererror.ScrapeErrors
7574
usages := make([]*deviceUsage, 0, len(partitions))
7675
for _, partition := range partitions {
7776
if !s.fsFilter.includePartition(partition) {
7877
continue
7978
}
8079
usage, usageErr := s.usage(partition.Mountpoint)
8180
if usageErr != nil {
82-
errors = append(errors, consumererror.NewPartialScrapeError(usageErr, 0))
81+
errors.Add(0, usageErr)
8382
continue
8483
}
8584

@@ -92,7 +91,7 @@ func (s *scraper) Scrape(_ context.Context) (pdata.MetricSlice, error) {
9291
appendSystemSpecificMetrics(metrics, 1, now, usages)
9392
}
9493

95-
err = scraperhelper.CombineScrapeErrors(errors)
94+
err = errors.Combine()
9695
if err != nil && len(usages) == 0 {
9796
partialErr := err.(consumererror.PartialScrapeError)
9897
partialErr.Failed = metricsLen

0 commit comments

Comments
 (0)