fix(tracing): treat resource detector errors as non-fatal

processOwnerDetector calls os/user.Current(), which fails in non-cgo
builds when the running UID has no /etc/passwd entry and $USER is unset
(common on hardened/distroless containers). It returns a plain error,
not ErrPartialResource, so the previous gate let the error escape and
SetupSDK aborted process startup.

Resource detection is documented as best-effort; downgrade any detector
error to a warning and fall back to an empty resource if the SDK returns
nil. The signature is preserved.
This commit is contained in:
2026-05-02 22:00:09 -07:00
parent 82e7f4398b
commit 988e07ade5
2 changed files with 16 additions and 10 deletions

View File

@@ -6,7 +6,6 @@ package otlpresource
import (
"context"
"errors"
"log/slog"
"go.opentelemetry.io/otel/attribute"
@@ -21,8 +20,11 @@ type Options struct {
Environment string // added as attribute "environment"
}
// New builds the shared OTel resource. Non-fatal partial/schema errors are
// logged via the provided slog logger and a usable resource is still returned.
// New builds the shared OTel resource. Resource detection is best-effort:
// any detector error is logged via the provided slog logger and a usable
// resource is still returned. The returned error is currently always nil; the
// signature is preserved so future callers can distinguish hard failures if
// the contract changes.
func New(ctx context.Context, log *slog.Logger, opts Options) (*resource.Resource, error) {
detectors := []resource.Option{
resource.WithFromEnv(), // OTEL_SERVICE_NAME / OTEL_RESOURCE_ATTRIBUTES
@@ -41,12 +43,16 @@ func New(ctx context.Context, log *slog.Logger, opts Options) (*resource.Resourc
resource.WithAttributes(attribute.String("environment", opts.Environment)))
}
// Detector failures (e.g. processOwnerDetector hitting user.Current() in
// a non-cgo build with no /etc/passwd entry and unset $USER) must not be
// fatal. Downgrade everything to a warning and fall back to an empty
// resource if the SDK returns nil.
res, err := resource.New(ctx, detectors...)
if errors.Is(err, resource.ErrPartialResource) || errors.Is(err, resource.ErrSchemaURLConflict) {
if log != nil {
log.Warn("otel resource setup", "err", err)
}
return res, nil
if err != nil && log != nil {
log.Warn("otel resource setup", "err", err)
}
return res, err
if res == nil {
res = resource.Empty()
}
return res, nil
}