Modernizes several `internal/*` packages under the Go proxy, replaces
third-party forks with standard-library primitives, and brings the
test suite from near-zero to high coverage across the touched packages.
Package changes
- **`internal/errors`** — Rewrites the `pkg/errors` fork as a thin
wrapper
over stdlib `errors`. A single `withStack` struct captures stack
traces via `runtime.Callers`; `fmt.Errorf("%w", ...)` handles all
message wrapping. Restores `errors.Is`/`As`/`Unwrap` chain traversal
(silently broken in the fork) and deletes ~190 lines of stack/frame
formatting. `Is`, `As`, `Unwrap`, and `Join` are re-exported so
callers need a single import.
- **`internal/logger`** — Swaps stdlib `log.Logger` for `log/slog` JSON
handlers with UTC timestamps and custom level labels (`verb`, `debug`,
`warn`, `error`). Hides `withContextID` (no external callers).
- **`internal/sync`** — Converts `Map[K, V]` from a concrete struct to
an interface with a `NewMap` constructor for testability.
- **`internal/signal`** — Adds `signalNotify` / `osExit` indirections so
`InstallSignals` and `InstallForceQuit` can be exercised without real
OS signals or process termination.
- **`internal/utils`** — Drops deprecated `io/ioutil` and the stdlib
`errors` alias (the internal `errors` package re-exports what's
needed).
- **`internal/version`** — No code changes; fully covered by new tests.
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
154 lines
3.5 KiB
Go
154 lines
3.5 KiB
Go
// Package errors provides error handling primitives with stack traces.
|
|
//
|
|
// It is a thin layer over the standard library's errors package, adding a
|
|
// stack trace at the point an error is created or wrapped. The wrapping
|
|
// chain is fully compatible with errors.Is, errors.As, and errors.Unwrap.
|
|
//
|
|
// # Adding context to an error
|
|
//
|
|
// _, err := io.ReadAll(r)
|
|
// if err != nil {
|
|
// return errors.Wrap(err, "read failed")
|
|
// }
|
|
//
|
|
// # Formatted printing of errors
|
|
//
|
|
// %s the error message (full wrap chain)
|
|
// %v same as %s
|
|
// %+v the error message followed by the captured stack trace
|
|
// %q the error message, quoted
|
|
//
|
|
// # Retrieving the stack trace
|
|
//
|
|
// Errors returned by this package satisfy the following interface:
|
|
//
|
|
// type stackTracer interface {
|
|
// StackTrace() []uintptr
|
|
// }
|
|
package errors
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"runtime"
|
|
)
|
|
|
|
// Re-exported stdlib primitives so callers can use a single import.
|
|
var (
|
|
Is = errors.Is
|
|
As = errors.As
|
|
Unwrap = errors.Unwrap
|
|
Join = errors.Join
|
|
)
|
|
|
|
// withStack wraps an error with a captured stack trace.
|
|
type withStack struct {
|
|
err error
|
|
pcs []uintptr
|
|
}
|
|
|
|
func (e *withStack) Error() string {
|
|
return e.err.Error()
|
|
}
|
|
|
|
func (e *withStack) Unwrap() error {
|
|
return e.err
|
|
}
|
|
|
|
func (e *withStack) StackTrace() []uintptr {
|
|
return e.pcs
|
|
}
|
|
|
|
func (e *withStack) Format(s fmt.State, verb rune) {
|
|
switch verb {
|
|
case 'v':
|
|
if s.Flag('+') {
|
|
fmt.Fprint(s, e.err.Error())
|
|
frames := runtime.CallersFrames(e.pcs)
|
|
for {
|
|
f, more := frames.Next()
|
|
fmt.Fprintf(s, "\n%s\n\t%s:%d", f.Function, f.File, f.Line)
|
|
if !more {
|
|
break
|
|
}
|
|
}
|
|
return
|
|
}
|
|
fallthrough
|
|
case 's':
|
|
fmt.Fprint(s, e.err.Error())
|
|
case 'q':
|
|
fmt.Fprintf(s, "%q", e.err.Error())
|
|
}
|
|
}
|
|
|
|
func callers() []uintptr {
|
|
var pcs [32]uintptr
|
|
n := runtime.Callers(3, pcs[:])
|
|
return pcs[:n]
|
|
}
|
|
|
|
func attach(err error) error {
|
|
return &withStack{err: err, pcs: callers()}
|
|
}
|
|
|
|
// New returns an error with the supplied message and a captured stack trace.
|
|
func New(message string) error {
|
|
return attach(errors.New(message))
|
|
}
|
|
|
|
// Errorf formats according to a format specifier and returns a new error with
|
|
// a captured stack trace. It supports %w for wrapping an existing error.
|
|
func Errorf(format string, args ...any) error {
|
|
return attach(fmt.Errorf(format, args...))
|
|
}
|
|
|
|
// WithStack annotates err with a stack trace at the point WithStack was called.
|
|
// If err is nil, WithStack returns nil.
|
|
func WithStack(err error) error {
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
return attach(err)
|
|
}
|
|
|
|
// WithMessage annotates err with a new message, without capturing a stack.
|
|
// If err is nil, WithMessage returns nil.
|
|
func WithMessage(err error, message string) error {
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
return fmt.Errorf("%s: %w", message, err)
|
|
}
|
|
|
|
// Wrap returns an error annotating err with a message and a captured stack.
|
|
// If err is nil, Wrap returns nil.
|
|
func Wrap(err error, message string) error {
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
return attach(fmt.Errorf("%s: %w", message, err))
|
|
}
|
|
|
|
// Wrapf is the formatting variant of Wrap.
|
|
// If err is nil, Wrapf returns nil.
|
|
func Wrapf(err error, format string, args ...any) error {
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
return attach(fmt.Errorf(fmt.Sprintf(format, args...)+": %w", err))
|
|
}
|
|
|
|
// Cause walks the error's Unwrap chain and returns the root error.
|
|
// New code should prefer errors.Is or errors.As.
|
|
func Cause(err error) error {
|
|
for err != nil {
|
|
u := errors.Unwrap(err)
|
|
if u == nil {
|
|
return err
|
|
}
|
|
err = u
|
|
}
|
|
return nil
|
|
}
|