You've already forked opentelemetry-go
							
							
				mirror of
				https://github.com/open-telemetry/opentelemetry-go.git
				synced 2025-10-31 00:07:40 +02:00 
			
		
		
		
	Wrap errors returned from Detect and New in sdk/resource (#3844)
				
					
				
			* Update Detect and New to wrap errors * Add TestNewWrapedError Test that New returns an error that can be unwrapped. * Add changes to changelog * Clarify and simplify errors
This commit is contained in:
		| @@ -11,6 +11,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm | ||||
| ### Changed | ||||
|  | ||||
| - Avoid creating new objects on all calls to `WithDeferredSetup` and `SkipContextSetup` in OpenTracing bridge. (#3833) | ||||
| - The `New` and `Detect` functions from `go.opentelemetry.io/otel/sdk/resource` return errors that wrap underlying errors instead of just containing the underlying error strings. (#3844) | ||||
|  | ||||
| ### Removed | ||||
|  | ||||
|   | ||||
| @@ -18,6 +18,7 @@ import ( | ||||
| 	"context" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"strings" | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| @@ -45,28 +46,65 @@ type Detector interface { | ||||
| // Detect calls all input detectors sequentially and merges each result with the previous one. | ||||
| // It returns the merged error too. | ||||
| func Detect(ctx context.Context, detectors ...Detector) (*Resource, error) { | ||||
| 	var autoDetectedRes *Resource | ||||
| 	var errInfo []string | ||||
| 	r := new(Resource) | ||||
| 	return r, detect(ctx, r, detectors) | ||||
| } | ||||
|  | ||||
| // detect runs all detectors using ctx and merges the result into res. This | ||||
| // assumes res is allocated and not nil, it will panic otherwise. | ||||
| func detect(ctx context.Context, res *Resource, detectors []Detector) error { | ||||
| 	var ( | ||||
| 		r    *Resource | ||||
| 		errs detectErrs | ||||
| 		err  error | ||||
| 	) | ||||
|  | ||||
| 	for _, detector := range detectors { | ||||
| 		if detector == nil { | ||||
| 			continue | ||||
| 		} | ||||
| 		res, err := detector.Detect(ctx) | ||||
| 		r, err = detector.Detect(ctx) | ||||
| 		if err != nil { | ||||
| 			errInfo = append(errInfo, err.Error()) | ||||
| 			errs = append(errs, err) | ||||
| 			if !errors.Is(err, ErrPartialResource) { | ||||
| 				continue | ||||
| 			} | ||||
| 		} | ||||
| 		autoDetectedRes, err = Merge(autoDetectedRes, res) | ||||
| 		r, err = Merge(res, r) | ||||
| 		if err != nil { | ||||
| 			errInfo = append(errInfo, err.Error()) | ||||
| 			errs = append(errs, err) | ||||
| 		} | ||||
| 		*res = *r | ||||
| 	} | ||||
|  | ||||
| 	var aggregatedError error | ||||
| 	if len(errInfo) > 0 { | ||||
| 		aggregatedError = fmt.Errorf("detecting resources: %s", errInfo) | ||||
| 	if len(errs) == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
| 	return autoDetectedRes, aggregatedError | ||||
| 	return errs | ||||
| } | ||||
|  | ||||
| type detectErrs []error | ||||
|  | ||||
| func (e detectErrs) Error() string { | ||||
| 	errStr := make([]string, len(e)) | ||||
| 	for i, err := range e { | ||||
| 		errStr[i] = fmt.Sprintf("* %s", err) | ||||
| 	} | ||||
|  | ||||
| 	format := "%d errors occurred detecting resource:\n\t%s" | ||||
| 	return fmt.Sprintf(format, len(e), strings.Join(errStr, "\n\t")) | ||||
| } | ||||
|  | ||||
| func (e detectErrs) Unwrap() error { | ||||
| 	switch len(e) { | ||||
| 	case 0: | ||||
| 		return nil | ||||
| 	case 1: | ||||
| 		return e[0] | ||||
| 	} | ||||
| 	return e[1:] | ||||
| } | ||||
|  | ||||
| func (e detectErrs) Is(target error) bool { | ||||
| 	return len(e) != 0 && errors.Is(e[0], target) | ||||
| } | ||||
|   | ||||
| @@ -17,7 +17,6 @@ package resource // import "go.opentelemetry.io/otel/sdk/resource" | ||||
| import ( | ||||
| 	"context" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"sync" | ||||
|  | ||||
| 	"go.opentelemetry.io/otel" | ||||
| @@ -51,17 +50,8 @@ func New(ctx context.Context, opts ...Option) (*Resource, error) { | ||||
| 		cfg = opt.apply(cfg) | ||||
| 	} | ||||
|  | ||||
| 	resource, err := Detect(ctx, cfg.detectors...) | ||||
|  | ||||
| 	var err2 error | ||||
| 	resource, err2 = Merge(resource, &Resource{schemaURL: cfg.schemaURL}) | ||||
| 	if err == nil { | ||||
| 		err = err2 | ||||
| 	} else if err2 != nil { | ||||
| 		err = fmt.Errorf("detecting resources: %s", []string{err.Error(), err2.Error()}) | ||||
| 	} | ||||
|  | ||||
| 	return resource, err | ||||
| 	r := &Resource{schemaURL: cfg.schemaURL} | ||||
| 	return r, detect(ctx, r, cfg.detectors) | ||||
| } | ||||
|  | ||||
| // NewWithAttributes creates a resource from attrs and associates the resource with a | ||||
|   | ||||
| @@ -452,6 +452,25 @@ func TestNew(t *testing.T) { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestNewWrapedError(t *testing.T) { | ||||
| 	localErr := errors.New("local error") | ||||
| 	_, err := resource.New( | ||||
| 		context.Background(), | ||||
| 		resource.WithDetectors( | ||||
| 			resource.StringDetector("", "", func() (string, error) { | ||||
| 				return "", localErr | ||||
| 			}), | ||||
| 			resource.StringDetector("", "", func() (string, error) { | ||||
| 				return "", assert.AnError | ||||
| 			}), | ||||
| 		), | ||||
| 	) | ||||
|  | ||||
| 	assert.ErrorIs(t, err, localErr) | ||||
| 	assert.ErrorIs(t, err, assert.AnError) | ||||
| 	assert.NotErrorIs(t, err, errors.New("false positive error")) | ||||
| } | ||||
|  | ||||
| func TestWithOSType(t *testing.T) { | ||||
| 	mockRuntimeProviders() | ||||
| 	t.Cleanup(restoreAttributesProviders) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user