Skip to content

feat: Change stack trace format to match runtime.Stack#5

Merged
tomoemon merged 2 commits into
mainfrom
feat-runtime-stack-format
Nov 10, 2025
Merged

feat: Change stack trace format to match runtime.Stack#5
tomoemon merged 2 commits into
mainfrom
feat-runtime-stack-format

Conversation

@tomoemon

@tomoemon tomoemon commented Nov 10, 2025

Copy link
Copy Markdown
Owner

Summary

Changed the default stack frame formatter to match runtime.Stack() format for better consistency with Go's standard stack traces and to eliminate dependency on source file availability at runtime.

Old Format vs New Format

Old format (required source files at runtime):

file.go:123 (0x456)
    functionName: sourceCodeLine

New format (runtime.Stack compatible):

functionName()
    file.go:123 +0x456

Motivation

The old format had a significant limitation: it attempted to read source files at runtime using os.Open() to display the actual source code line. This caused issues in production environments where:

  1. Source files are not available - Deployed binaries typically don't include source code
  2. Silent degradation - When source files couldn't be read, the formatter would silently skip the source line, resulting in inconsistent output
  3. Missing function names - If the source couldn't be read, function names weren't displayed at all

Example of the problem:

no user exists with the given uid
github.com/example/project/infra/auth.go:85 (0x125e08a)
github.com/example/project/application/service.go:262 (0x1265468)

Notice: No function names or source code lines are visible because the source files weren't available in the deployed environment.

Benefits of New Format

1. Consistent Output

Function names are always displayed, regardless of source file availability:

github.com/example/project/infra.(*AuthClient).GetUser()
    /path/to/auth.go:85 +0x125e08a
github.com/example/project/application.(*Service).Calculate()
    /path/to/service.go:262 +0x1265468

2. Runtime.Stack Compatibility

Matches the exact format used by Go's runtime/debug.Stack(), making it familiar to all Go developers and compatible with existing log parsing tools.

3. No Runtime Dependencies

No longer attempts to read source files at runtime - all information comes from the compiled binary's debug symbols.

4. Cleaner Output

More concise format that's easier to read in logs:

  • Function name on first line
  • File path and offset on indented second line

Changes

Code Changes

stackframe_formatter.go:

// Old implementation
func defaultStackFrameFormatter(frame *StackFrame) string {
    str := fmt.Sprintf("%s:%d (0x%x)\n", frame.File, frame.LineNumber, frame.ProgramCounter)
    source, err := frame.SourceLine()  // ❌ Tries to read source file
    if err != nil {
        return str
    }
    return str + fmt.Sprintf("\t%s: %s\n", frame.Name, source)
}

// New implementation
func defaultStackFrameFormatter(frame *StackFrame) string {
    // Format: FunctionName()
    //     file/path.go:123 +0xhex
    return fmt.Sprintf("%s()\n\t%s:%d +0x%x\n", 
        frame.Name, frame.File, frame.LineNumber, frame.ProgramCounter)
}

README.md:

  • Updated all stack trace examples to show new format
  • Updated format documentation
  • All output examples now show function names

Example Output

Before (in production, source unavailable):

database error
/path/to/file.go:42 (0x1234567)
/path/to/file.go:30 (0x7654321)

After:

database error
main.myFunction()
    /path/to/file.go:42 +0x1234567
main.main()
    /path/to/file.go:30 +0x7654321

Testing

All existing tests pass with the new format:

$ go test ./...
ok      github.com/tomoemon/go-errstk          0.276s
ok      github.com/tomoemon/go-errstk/errstklint    0.667s

Backwards Compatibility

This is a format change that affects the string representation of stack traces. Users who:

  • Parse stack traces programmatically may need to update their parsers
  • Can customize the format using DefaultStackFrameFormatter if they need the old behavior
  • Can still access all stack frame data via WalkStack() API for custom formatting

🤖 Generated with Claude Code

tomoemon and others added 2 commits November 10, 2025 16:33
Changed the default stack frame formatter to match the format used by
runtime.Stack() for better consistency with Go's standard stack traces.

Old format:
  file.go:123 (0x456)
      functionName: sourceCodeLine

New format:
  functionName()
      file.go:123 +0x456

Benefits:
- Matches runtime.Stack() format exactly
- No dependency on source file availability at runtime
- Cleaner and more concise output
- Function names always visible (previously only shown with source)

Changes:
- Updated defaultStackFrameFormatter to new format
- Removed source code line reading from default formatter
- Updated all README examples to show new format
- All tests pass with new format

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Package names were missing from stack trace function names.
This fix concatenates Package and Name fields to show the full
qualified function name, matching runtime.Stack() output format.

Example output:
- Before: (*Repository).Get()
- After: main.(*Repository).Get()
@tomoemon
tomoemon merged commit 3ecbd6e into main Nov 10, 2025
1 check passed
@tomoemon
tomoemon deleted the feat-runtime-stack-format branch November 10, 2025 09:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant