Skip to content

Add an internal/azderrors package to capture error origin frames #8077

@JeffreyCA

Description

@JeffreyCA

Phase 4 of #8011. Depends on #8017.

Summary

Introduce a small in-tree internal/azderrors package that captures a single call-site frame at error construction/wrap time, and emit it as additive span attributes (error.origin.func, error.origin.file, error.origin.line).

This directly answers the user-facing "where in azd did this come from?" question without adopting any external library. Single-frame capture via runtime.Caller(1) is ~100ns per call and zero allocations beyond the error struct itself.

A future Phase 7 issue (#8078) considers a full return-path / stacktrace solution if single-frame coverage proves insufficient.

API surface

package azderrors

type Error struct {
    cls   Classification // from internal/tracing/errclassify
    cause error
    msg   string
    pc    [1]uintptr     // single-frame call-site
}

func (e *Error) Error() string             // full message
func (e *Error) Unwrap() error             // cause
func (e *Error) Classify() Classification  // for the shared classifier
func (e *Error) Frame() runtime.Frame      // resolved on demand

func New(cls Classification, format string, args ...any) error
func Wrap(err error, cls Classification, format string, args ...any) error
func Errorf(cls Classification, format string, args ...any) error
func WithCode(err error, code string) error

Each constructor calls runtime.Callers(2, e.pc[:]) so e.Frame() returns the caller of New/Wrap/Errorf. Frames are resolved lazily when the classifier reads them.

Compatibility with stdlib errors is full: errors.Is, errors.As, errors.AsType[*azderrors.Error], errors.Unwrap all behave normally.

Telemetry attributes

When the chain contains an *azderrors.Error with a captured frame, emit:

Key Type Source Example
error.origin.func string runtime.Frame.Function github.com/azure/azure-dev/cli/azd/pkg/project.(*Manager).Load
error.origin.file string module-relative path pkg/project/manager.go
error.origin.line int runtime.Frame.Line 142

Walk the chain outermost-first; emit the first *azderrors.Error frame found.

Resolve runtime.Frame.File to a module-relative path via runtime/debug.ReadBuildInfo so dev-build telemetry is consistent with release builds (which already strip absolute paths via -trimpath). Fall back to filepath.Base(frame.File) if module info is unavailable.

Scope

Tests

  • Unit tests for New/Wrap/Errorf frame capture (assert correct file/line).
  • Unit tests for the chain walker that picks up the first *azderrors.Error frame.
  • AppInsights serialization tests for the three new attributes.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/error-handlingError suggestions, error frameworkarea/telemetryTelemetry, tracing, observabilitybugSomething isn't working

    Type

    No fields configured for Bug.

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions