stacktrace

package
v0.0.0-...-591fb0a Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 9, 2026 License: MIT Imports: 4 Imported by: 0

README

errx/stacktrace

Optional stack trace support for errx errors.

Overview

The stacktrace package extends errx with stack trace capabilities while keeping the core errx package minimal and zero-dependency. It provides two usage patterns:

  1. Per-error opt-in using Here() as a Classified
  2. Automatic capture using stacktrace.Wrap() and stacktrace.Classify()

Installation

go get github.com/go-extras/errx/stacktrace@latest

Usage

Option 1: Per-Error Opt-In

Use Here() to capture stack traces only where needed:

import (
    "github.com/go-extras/errx"
    "github.com/go-extras/errx/stacktrace"
)

var ErrNotFound = errx.NewSentinel("not found")

// Capture stack trace at this specific error site
err := errx.Wrap("operation failed", cause, ErrNotFound, stacktrace.Here())
Option 2: Automatic Capture

Use stacktrace.Wrap() or stacktrace.Classify() for automatic trace capture:

// Automatically captures stack trace
err := stacktrace.Wrap("operation failed", cause, ErrNotFound)

// Or with Classify
err := stacktrace.Classify(cause, ErrRetryable)
Extracting Stack Traces

Extract and use stack traces from any error in the chain:

frames := stacktrace.Extract(err)
if frames != nil {
    for _, frame := range frames {
        fmt.Printf("%s:%d %s\n", frame.File, frame.Line, frame.Function)
    }
}

Integration with errx Features

Stack traces work seamlessly with all errx features:

var ErrNotFound = errx.NewSentinel("not found")

// Combine stack traces with displayable errors and attributes
displayErr := errx.NewDisplayable("User not found")
attrErr := errx.WithAttrs("user_id", "12345", "action", "fetch")

err := stacktrace.Wrap("failed to get user profile",
    errx.Classify(displayErr, ErrNotFound, attrErr))

// All features work together
fmt.Println("Error:", err.Error())
fmt.Println("Displayable:", errx.DisplayText(err))
fmt.Println("Is not found:", errors.Is(err, ErrNotFound))
fmt.Println("Has attributes:", errx.HasAttrs(err))
fmt.Println("Has stack trace:", stacktrace.Extract(err) != nil)

API

Functions
  • Here() errx.Classified - Captures the current stack trace as a Classified
  • Extract(err error) []Frame - Extracts stack frames from an error chain
  • Wrap(text string, cause error, classifications ...errx.Classified) error - Wraps with automatic trace
  • Classify(cause error, classifications ...errx.Classified) error - Classifies with automatic trace
Types
  • Frame - Represents a single stack frame with File, Line, and Function fields

Performance Considerations

Stack trace capture has a small performance cost (~2-10µs per capture):

  • Uses runtime.Callers to walk the stack
  • Allocates a slice for program counters
  • Frame resolution is done lazily on Extract()

Recommendations:

  • Use per-error opt-in (Here()) in hot paths
  • Use automatic capture (stacktrace.Wrap()) in application code
  • Libraries should use core errx; applications add traces as needed

Design Philosophy

This package follows Option 6 from the errx tracing design:

  1. Core stays minimal: errx remains zero-dependency and fast
  2. Opt-in granularity: Choose per-error or blanket trace capture
  3. Composable: Traces are just another Classified, fitting existing patterns
  4. Library-friendly: Libraries use errx core; applications add tracing where needed

Examples

See the package documentation for more examples.

License

MIT License - see the LICENSE file for details.

Documentation

Overview

Package stacktrace provides optional stack trace support for errx errors.

This package extends errx with stack trace capabilities while keeping the core errx package minimal and zero-dependency. It offers two usage patterns:

  1. Per-error opt-in using Here() as a Classified: err := errx.Wrap("context", cause, ErrNotFound, stacktrace.Here())

  2. Convenience functions that automatically capture traces: err := stacktrace.Wrap("context", cause, ErrNotFound)

Stack traces can be extracted from any error in the chain using Extract():

frames := stacktrace.Extract(err)
for _, frame := range frames {
    fmt.Printf("%s:%d %s\n", frame.File, frame.Line, frame.Function)
}
Example (Integration)

Example_integration demonstrates combining stacktrace with other errx features

package main

import (
	"errors"
	"fmt"

	"github.com/go-extras/errx"
	"github.com/go-extras/errx/stacktrace"
)

func main() {
	var ErrNotFound = errx.NewSentinel("not found")

	// Combine stack traces with displayable errors and attributes
	displayErr := errx.NewDisplayable("User not found")
	attrErr := errx.WithAttrs("user_id", "12345", "action", "fetch")

	err := stacktrace.Wrap("failed to get user profile",
		errx.Classify(displayErr, ErrNotFound, attrErr))

	// All features work together
	fmt.Println("Error:", err.Error())
	fmt.Println("Displayable:", errx.DisplayText(err))
	fmt.Println("Is not found:", errors.Is(err, ErrNotFound))
	fmt.Println("Has attributes:", errx.HasAttrs(err))
	fmt.Println("Has stack trace:", stacktrace.Extract(err) != nil)

}
Output:

Error: failed to get user profile: User not found
Displayable: User not found
Is not found: true
Has attributes: true
Has stack trace: true

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Classify

func Classify(cause error, classifications ...errx.Classified) error

Classify attaches one or more classifications to an error, automatically capturing a stack trace at the call site.

This is a convenience function equivalent to:

errx.Classify(cause, append(classifications, stacktrace.Here())...)

If cause is nil, Classify returns nil.

Example:

err := stacktrace.Classify(cause, ErrNotFound)
Example

ExampleClassify demonstrates using stacktrace.Classify

package main

import (
	"errors"
	"fmt"

	"github.com/go-extras/errx"
	"github.com/go-extras/errx/stacktrace"
)

func main() {
	var ErrRetryable = errx.NewSentinel("retryable error")

	// stacktrace.Classify adds classification and trace without changing the message
	baseErr := errors.New("temporary network failure")
	err := stacktrace.Classify(baseErr, ErrRetryable)

	// Original message is preserved
	fmt.Println(err.Error())

	// But classification and trace are added
	fmt.Println("Is retryable:", errors.Is(err, ErrRetryable))
	fmt.Println("Has trace:", stacktrace.Extract(err) != nil)

}
Output:

temporary network failure
Is retryable: true
Has trace: true

func Here

func Here() errx.Classified

Here captures the current stack trace and returns it as an errx.Classified. It can be used with errx.Wrap() or errx.Classify() to attach stack traces to errors.

The stack trace is captured starting from the caller of Here(), skipping the Here() function itself and the runtime.Callers call.

Example:

err := errx.Wrap("operation failed", cause, ErrNotFound, stacktrace.Here())

The captured stack trace can later be extracted using Extract().

Example

ExampleHere demonstrates using Here() to capture stack traces per-error

package main

import (
	"errors"
	"fmt"

	"github.com/go-extras/errx"
	"github.com/go-extras/errx/stacktrace"
)

func main() {
	var ErrNotFound = errx.NewSentinel("not found")

	// Capture stack trace at this specific error site
	baseErr := errors.New("user record missing")
	err := errx.Wrap("failed to fetch user", baseErr, ErrNotFound, stacktrace.Here())

	// Extract and display the stack trace
	frames := stacktrace.Extract(err)
	if frames != nil {
		fmt.Printf("Stack trace captured: %d frames\n", len(frames))
		// In real code, you might log all frames
		if len(frames) > 0 {
			fmt.Printf("Top frame: %s\n", frames[0].Function)
		}
	}

}
Output:

Stack trace captured: 7 frames
Top frame: github.com/go-extras/errx/stacktrace_test.ExampleHere

func Wrap

func Wrap(text string, cause error, classifications ...errx.Classified) error

Wrap wraps an error with additional context text and optional classifications, automatically capturing a stack trace at the call site.

This is a convenience function equivalent to:

errx.Wrap(text, cause, append(classifications, stacktrace.Here())...)

If cause is nil, Wrap returns nil.

Example:

err := stacktrace.Wrap("failed to process order", cause, ErrNotFound)
Example

ExampleWrap demonstrates using stacktrace.Wrap for automatic trace capture

package main

import (
	"errors"
	"fmt"

	"github.com/go-extras/errx"
	"github.com/go-extras/errx/stacktrace"
)

func main() {
	var ErrDatabase = errx.NewSentinel("database error")

	// stacktrace.Wrap automatically captures the stack trace
	baseErr := errors.New("connection timeout")
	err := stacktrace.Wrap("database query failed", baseErr, ErrDatabase)

	// The error works like a normal errx error
	fmt.Println(err.Error())
	fmt.Println("Is database error:", errors.Is(err, ErrDatabase))

	// But also has a stack trace
	frames := stacktrace.Extract(err)
	fmt.Println("Has stack trace:", frames != nil)

}
Output:

database query failed: connection timeout
Is database error: true
Has stack trace: true

Types

type Frame

type Frame struct {
	File     string // Full path to the source file
	Line     int    // Line number in the source file
	Function string // Fully qualified function name
}

Frame represents a single stack frame with file, line, and function information.

func Extract

func Extract(err error) []Frame

Extract returns stack frames from the first traced error found in the error chain. It traverses the entire error chain looking for a traced error and returns its frames.

Returns nil if the error is nil or does not contain any stack trace.

Example:

frames := stacktrace.Extract(err)
if frames != nil {
    for _, frame := range frames {
        fmt.Printf("%s:%d %s\n", frame.File, frame.Line, frame.Function)
    }
}
Example

ExampleExtract demonstrates extracting and formatting stack traces

package main

import (
	"errors"
	"fmt"

	"github.com/go-extras/errx/stacktrace"
)

func main() {
	// Create an error with a stack trace
	err := stacktrace.Wrap("operation failed", errors.New("base error"))

	// Extract the stack trace
	frames := stacktrace.Extract(err)
	if frames != nil {
		fmt.Printf("Stack trace (%d frames):\n", len(frames))
		for i, frame := range frames {
			if i >= 3 { // Limit output for example
				fmt.Println("  ...")
				break
			}
			fmt.Printf("  %s:%d\n", frame.Function, frame.Line)
		}
	}

}
Output:

Stack trace (7 frames):
  github.com/go-extras/errx/stacktrace_test.ExampleExtract:80
  testing.runExample:63
  testing.runExamples:41
  ...
Example (NoTrace)

ExampleExtract_noTrace demonstrates Extract returning nil for errors without traces

package main

import (
	"errors"
	"fmt"

	"github.com/go-extras/errx/stacktrace"
)

func main() {
	// Regular error without stack trace
	err := errors.New("simple error")

	frames := stacktrace.Extract(err)
	fmt.Printf("Stack trace: %v\n", frames)

}
Output:

Stack trace: []

func (Frame) String

func (f Frame) String() string

String returns a formatted representation of the frame.

Example

ExampleFrame_String demonstrates formatting a stack frame

package main

import (
	"fmt"

	"github.com/go-extras/errx/stacktrace"
)

func main() {
	frame := stacktrace.Frame{
		File:     "/home/user/project/main.go",
		Line:     42,
		Function: "main.processRequest",
	}

	fmt.Println(frame.String())

}
Output:

/home/user/project/main.go:42 main.processRequest

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL