Skip to content

Commit f98a74a

Browse files
committed
Update README
1 parent cbf4a73 commit f98a74a

1 file changed

Lines changed: 73 additions & 31 deletions

File tree

README.md

Lines changed: 73 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# go-xerrors
22

3+
[![Go Reference](https://pkg.go.dev/badge/github.com/mdobak/go-xerrors.svg)](https://pkg.go.dev/github.com/mdobak/go-xerrors) [![Go Report Card](https://goreportcard.com/badge/github.com/mdobak/go-xerrors)](https://goreportcard.com/report/github.com/mdobak/go-xerrors) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
4+
35
`go-xerrors` is a simple, idiomatic, lightweight Go package that provides utilities for error handling. It offers functions and types to support stack traces, multi-errors, and simplified panic handling. The package is compatible with Go's standard error handling mechanisms, such as `errors.As`, `errors.Is`, and `errors.Unwrap`, including features from Go 1.13 and 1.20.
46

57
**Main Features:**
@@ -20,6 +22,49 @@ go get -u github.com/mdobak/go-xerrors
2022

2123
## Usage
2224

25+
### Example
26+
27+
The following example demonstrates the basic usage of `go-xerrors` for creating and handling errors.
28+
29+
```go
30+
package main
31+
32+
import (
33+
"database/sql"
34+
"fmt"
35+
36+
"github.com/mdobak/go-xerrors"
37+
)
38+
39+
func findUserByID(id int) error {
40+
// Simulate a standard library error.
41+
err := sql.ErrNoRows
42+
43+
// Wrap the original error with additional context and capture a stack trace
44+
// at this point in the call stack.
45+
return xerrors.Newf("user %d not found: %w", id, err)
46+
}
47+
48+
func main() {
49+
err := findUserByID(123)
50+
if err != nil {
51+
// 1. The standard Error() method provides a concise, log-friendly message.
52+
fmt.Println("Concise log message:", err.Error())
53+
// Output: user 123 not found: sql: no rows in result set
54+
55+
// 2. xerrors.Print provides a rich, multi-line report for developers,
56+
// including the stack trace from where the error was wrapped.
57+
xerrors.Print(err)
58+
// Output:
59+
// Error: user 123 not found: sql: no rows in result set
60+
// at main.findUserByID (/home/user/app/main.go:15)
61+
// at main.main (/home/user/app/main.go:20)
62+
// at runtime.main (/usr/local/go/src/runtime/proc.go:250)
63+
// at runtime.goexit (/usr/local/go/src/runtime/asm_amd64.s:1594)
64+
}
65+
}
66+
```
67+
2368
### Creating Errors with Stack Traces
2469

2570
The primary way to create an error in `go-xerrors` is by using the `xerrors.New` or `xerrors.Newf` functions:
@@ -53,7 +98,7 @@ Error: something went wrong
5398

5499
### Working with Stack Traces
55100

56-
To retrieve only the stack trace information programmatically:
101+
To retrieve the stack trace information programmatically:
57102

58103
```go
59104
trace := xerrors.StackTrace(err)
@@ -95,7 +140,7 @@ if err != nil {
95140
}
96141
```
97142

98-
Note that wrapping multiple errors with `xerrors.Newf` is possible on Go 1.20 and later.
143+
Note that wrapping multiple errors with `xerrors.Newf` is possible only in Go 1.20 and later.
99144

100145
### Creating Error Chains Without Stack Traces
101146

@@ -111,13 +156,13 @@ With formatted messages:
111156
err := xerrors.Joinf("operation failed: %w", otherErr)
112157
```
113158

114-
Note that wrapping multiple errors with `xerrors.Joinf` is possible on Go 1.20 and later.
159+
Note that wrapping multiple errors with `xerrors.Joinf` is possible only in Go 1.20 and later.
115160

116161
The main difference between Go's `fmt.Errorf` and `xerrors.Newf`/`xerrors.Joinf` is that the latter functions preserve the error chain, whereas `fmt.Errorf` flattens it (i.e., its `Unwrap` method returns all underlying errors at once instead of just the next one in the chain).
117162

118163
### Sentinel Errors
119164

120-
Sentinel errors are predefined error values representing specific, known failure conditions. `go-xerrors` provides `xerrors.Message` to create distinct sentinel error values:
165+
Sentinel errors are predefined, exported error values used to signal specific, well-known conditions (e.g., `io.EOF`). The `go-xerrors` package provides the `xerrors.Message` and `xerrors.Messagef` functions to create distinct sentinel error values:
121166

122167
```go
123168
var ErrAccessDenied = xerrors.Message("access denied")
@@ -166,26 +211,24 @@ if err != nil {
166211
// Output:
167212
// Error: [username cannot be empty, password must be at least 8 characters]
168213
// 1. Error: username cannot be empty
169-
// at main.validateInput (/path/to/your/file.go:XX)
170-
// ... stack trace ...
214+
// at main.validateInput (/path/to/your/file.go:XX)
215+
// ... stack trace ...
171216
// 2. Error: password must be at least 8 characters
172-
// at main.validateInput (/path/to/your/file.go:YY)
173-
// ... stack trace ...
217+
// at main.validateInput (/path/to/your/file.go:YY)
218+
// ... stack trace ...
174219
}
175220
```
176221

177222
The resulting multi-error implements the standard `error` interface as well as `errors.Is`, `errors.As`, and `errors.Unwrap`, allowing you to check for specific errors or extract them.
178223

179224
**Comparison with Go 1.20 `errors.Join`:**
180225

181-
Go 1.20 introduced `errors.Join` for error aggregation. While serving a similar purpose, `xerrors.Append` offers:
182-
183-
1. **Individual Stack Traces**: Preserves the individual stack traces associated with each appended error
184-
2. **Enhanced Formatting**: Provides detailed, structured output for multi-errors
185-
3. **Consistent `Error()` Output**: Produces a concise, single-line summary
226+
Go 1.20 introduced `errors.Join` for error aggregation. While it serves a similar purpose, `xerrors.Append` preserves the individual stack traces associated with each appended error and adheres to the convention of returning a single line from the `Error()` method.
186227

187228
### Simplified Panic Handling
188229

230+
Panics can be challenging to locate and handle effectively in Go applications, especially when using `recover()`. Common issues, such as nil pointer dereferences or out-of-bounds slice accesses, often result in unclear panic messages. Without a stack trace, pinpointing the origin of the panic can be difficult.
231+
189232
`go-xerrors` provides utilities to convert panic values into proper errors with stack traces.
190233

191234
**Using `xerrors.Recover`:**
@@ -221,13 +264,13 @@ func handleTask() (err error) {
221264

222265
The returned error implements the `PanicError` interface, which provides access to the original panic value via the `Panic()` method.
223266

224-
### When to use `New`, `Join`, or `Append`
267+
### Choosing Between `New`, `Join`, and `Append`
225268

226-
While all three functions can be used to aggregate errors, they serve different purposes:
269+
While these functions can all be used to aggregate errors, they each serve distinct purposes:
227270

228-
- **`xerrors.New`**: Create errors with stack traces, useful for wrapping existing errors to add context
229-
- **`xerrors.Join`**: Create chained errors without stack traces, useful for defining sentinel errors
230-
- **`xerrors.Append`**: Create multi-errors by aggregating independent errors
271+
- **`xerrors.New`**: Use this to create errors and attach stack traces, especially when wrapping existing errors to provide additional context.
272+
- **`xerrors.Join`**: Use this to chain errors together _without_ capturing stack traces. This is most appropriate for creating sentinel errors.
273+
- **`xerrors.Append`**: Use this to aggregate multiple, independent errors into a single multi-error. This is useful when several operations might fail, and you want to report all failures at once.
231274

232275
#### Examples
233276

@@ -248,10 +291,10 @@ func (m *MyStruct) MarshalJSON() ([]byte, error) {
248291

249292
```go
250293
var (
251-
// Using xerrors.Join lets us create sentinel errors that can be
294+
// Using xerrors.Join allows us to create sentinel errors that can be
252295
// checked with errors.Is against both ErrValidation and the
253296
// specific validation error. We do not want to capture a stack trace
254-
// here; hence, we use xerrors.Join instead of xerrors.New.
297+
// here; therefore, we use xerrors.Join instead of xerrors.New.
255298
ErrValidation = xerrors.Message("validation error")
256299
ErrInvalidName = xerrors.Join(ErrValidation, "name is invalid")
257300
ErrInvalidAge = xerrors.Join(ErrValidation, "age is invalid")
@@ -300,28 +343,27 @@ func (m *MyStruct) Validate() error {
300343
- `xerrors.Joinf(format string, args ...any) error`: Creates a formatted chained error without a stack trace
301344
- `xerrors.Message(message string) error`: Creates a simple sentinel error
302345
- `xerrors.Messagef(format string, args ...any) error`: Creates a simple formatted sentinel error
303-
304-
### Multi-Error Functions
305-
306346
- `xerrors.Append(err error, errs ...error) error`: Aggregates errors into a multi-error
307347

308-
### Panic Handling
348+
### Panics
309349

310350
- `xerrors.Recover(callback func(err error))`: Recovers from panics and invokes a callback with the error
311351
- `xerrors.FromRecover(recoveredValue any) error`: Converts a recovered value to an error with a stack trace
312352

313-
### Formatting Functions
353+
### Stack Trace
354+
355+
- `xerrors.StackTrace(err error) Callers`: Extracts the stack trace from an error
356+
- `xerrors.WithStackTrace(err error, skip int) error`: Wraps an error with a stack trace
357+
- `DefaultCallersFormatter`: The default formatter for [Callers], used when printing stack traces.
358+
- `DefaultFrameFormatter`: The default formatter for [Frame], used when printing stack traces.
359+
360+
### Error printing
314361

315362
- `xerrors.Print(err error)`: Prints a formatted error to stderr
316363
- `xerrors.Sprint(err error) string`: Returns a formatted error as a string
317364
- `xerrors.Fprint(w io.Writer, err error)`: Writes a formatted error to the provided writer
318365

319-
### Stack Trace Functions
320-
321-
- `xerrors.StackTrace(err error) Callers`: Extracts the stack trace from an error
322-
- `xerrors.WithStackTrace(err error, skip int) error`: Wraps an error with a stack trace
323-
324-
### Key Interfaces
366+
### Interfaces
325367

326368
- `DetailedError`: For errors that provide detailed information
327369
- `PanicError`: For errors created from panic values with access to the original panic value

0 commit comments

Comments
 (0)