-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Rahugg
committed
Nov 22, 2024
0 parents
commit 33c90cf
Showing
8 changed files
with
515 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
name: Go CI | ||
|
||
on: | ||
push: | ||
branches: [ master ] | ||
pull_request: | ||
branches: [ master ] | ||
|
||
jobs: | ||
build: | ||
name: Build and Test | ||
runs-on: ubuntu-latest | ||
strategy: | ||
matrix: | ||
go-version: [ '1.22.x' ] | ||
|
||
steps: | ||
- uses: actions/checkout@v4 | ||
|
||
- name: Set up Go ${{ matrix.go-version }} | ||
uses: actions/setup-go@v4 | ||
with: | ||
go-version: ${{ matrix.go-version }} | ||
cache: true | ||
|
||
- name: Install dependencies | ||
run: go mod download | ||
|
||
- name: Verify dependencies | ||
run: go mod verify | ||
|
||
- name: Run go vet | ||
run: go vet ./... | ||
|
||
- name: Run tests with coverage | ||
run: go test ./... -v -race -coverprofile=coverage.txt -covermode=atomic | ||
|
||
- name: Upload coverage reports to Codecov | ||
uses: codecov/codecov-action@v3 | ||
env: | ||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} | ||
|
||
- name: Build | ||
run: go build -v ./... | ||
|
||
- name: Run golangci-lint | ||
uses: golangci/golangci-lint-action@v3 | ||
with: | ||
version: latest |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
# go-stacktrace | ||
|
||
`go-stacktrace` is a robust Go package for enhanced error handling, providing stack traces, error wrapping, and user-friendly error messages with optional color formatting. | ||
|
||
## Features | ||
|
||
- 🔍 Detailed stack traces for improved debugging | ||
- 📝 Custom user messages and payload support | ||
- 🎨 Colorized error output (configurable) | ||
- 🔄 Error wrapping with context preservation | ||
- 🪶 Lightweight with zero external dependencies | ||
- 🧪 Comprehensive test coverage | ||
|
||
## Installation | ||
|
||
```bash | ||
go get github.com/Rahugg/go-stacktrace | ||
``` | ||
|
||
## Usage | ||
|
||
### Basic Error Wrapping | ||
|
||
```go | ||
import "github.com/Rahugg/go-stacktrace/errorhandler" | ||
|
||
func main() { | ||
err := someFunction() | ||
if err != nil { | ||
wrappedErr := errorhandler.WrapError( | ||
err, | ||
"operation-failed", // payload | ||
"Failed to process request" // user message | ||
) | ||
fmt.Println(errorhandler.FailOnError(wrappedErr)) | ||
} | ||
} | ||
``` | ||
|
||
### Controlling Color Output | ||
|
||
```go | ||
// Disable colored output | ||
errorhandler.SetEnableColors(false) | ||
|
||
// Enable colored output (default) | ||
errorhandler.SetEnableColors(true) | ||
``` | ||
|
||
### Error Information | ||
|
||
The wrapped error includes: | ||
- Original error message | ||
- User-friendly message | ||
- Custom payload (optional) | ||
- Stack trace | ||
- Color-coded output (optional) | ||
|
||
## Example Output | ||
|
||
``` | ||
Payload: operation-failed | ||
Error Details | ||
Original Error: file not found | ||
User Message: Failed to process request | ||
Stack Trace | ||
main.someFunction | ||
/path/to/file.go:25 | ||
main.main | ||
/path/to/main.go:12 | ||
``` | ||
|
||
## Features in Detail | ||
|
||
### TracedError Structure | ||
```go | ||
type TracedError struct { | ||
OriginalError error | ||
UserMessage string | ||
Payload string | ||
StackTrace []uintptr | ||
} | ||
``` | ||
|
||
### Available Colors | ||
- Red: Error messages | ||
- Green: User messages | ||
- Yellow: Payload information | ||
- Blue: Function names in stack trace | ||
- Magenta: Stack trace headers | ||
- Cyan: Section headers | ||
|
||
## Contributing | ||
|
||
Contributions are welcome! Please feel free to submit a Pull Request. | ||
Or DM me. | ||
|
||
## License | ||
|
||
This project is licensed under the MIT License - see the LICENSE file for details. | ||
|
||
## Project Structure | ||
``` | ||
. | ||
├── errorhandler | ||
│ ├── colors.go # Color constants | ||
│ ├── errorhandler.go # Core functionality | ||
│ └── errorhandler_test.go # Test suite | ||
├── example | ||
│ └── main.go # Usage examples | ||
├── go.mod | ||
├── go.yml | ||
└── README.md | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package errorhandler | ||
|
||
const ( | ||
// Reset sequence | ||
Reset = "\033[0m" | ||
|
||
// Black Regular colors | ||
Black = "\033[30m" | ||
Red = "\033[31m" | ||
Green = "\033[32m" | ||
Yellow = "\033[33m" | ||
Blue = "\033[34m" | ||
Magenta = "\033[35m" | ||
Cyan = "\033[36m" | ||
White = "\033[37m" | ||
|
||
// BrightBlack Bright colors for better visibility | ||
BrightBlack = "\033[90m" | ||
BrightRed = "\033[91m" | ||
BrightGreen = "\033[92m" | ||
BrightYellow = "\033[93m" | ||
BrightBlue = "\033[94m" | ||
BrightMagenta = "\033[95m" | ||
BrightCyan = "\033[96m" | ||
BrightWhite = "\033[97m" | ||
|
||
// BgRed Background colors (if needed) | ||
BgRed = "\033[41m" | ||
BgGreen = "\033[42m" | ||
BgYellow = "\033[43m" | ||
BgBlue = "\033[44m" | ||
BgMagenta = "\033[45m" | ||
BgCyan = "\033[46m" | ||
BgWhite = "\033[47m" | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
package errorhandler | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"runtime" | ||
"strings" | ||
) | ||
|
||
// EnableColors determines whether colored output is enabled. | ||
var EnableColors = true | ||
|
||
// TracedError represents an error with additional context, user-friendly messages, and stack trace information. | ||
type TracedError struct { | ||
OriginalError error | ||
UserMessage string | ||
Payload string | ||
StackTrace []uintptr | ||
} | ||
|
||
// Error implements the error interface for TracedError, returning a formatted error message. | ||
func (te *TracedError) Error() string { | ||
return fmt.Sprintf("%s\nUser Message: %s", te.OriginalError.Error(), te.UserMessage) | ||
} | ||
|
||
// FailOnError formats the error message for any given error. | ||
// If the error is a TracedError, it includes additional context. | ||
func FailOnError(err error) string { | ||
if err == nil { | ||
return "" | ||
} | ||
|
||
var tracedErr *TracedError | ||
if errors.As(err, &tracedErr) { | ||
return tracedErr.formatError() | ||
} | ||
|
||
return err.Error() | ||
} | ||
|
||
// captureCallers retrieves the call stack up to a certain depth. | ||
func captureCallers() []uintptr { | ||
const stackDepth = 64 | ||
var pcs [stackDepth]uintptr | ||
n := runtime.Callers(3, pcs[:]) | ||
return pcs[:n] | ||
} | ||
|
||
func formatWithColor(color, text string) string { | ||
if EnableColors { | ||
return fmt.Sprintf("%s%s%s", color, text, Reset) | ||
} | ||
return text | ||
} | ||
|
||
// formatError formats the TracedError for detailed display, including payload, user message, and stack trace. | ||
// optionally using colors for better readability. | ||
func (te *TracedError) formatError() string { | ||
var sb strings.Builder | ||
|
||
// Apply payload if available | ||
if te.Payload != "" { | ||
// Using BrightYellow for better visibility of metadata | ||
sb.WriteString(fmt.Sprintf("%s: %s\n", formatWithColor(BrightYellow, "Payload"), te.Payload)) | ||
} | ||
|
||
// Add error details header (using white for headers) | ||
sb.WriteString(fmt.Sprintf("%s\n", formatWithColor(BrightWhite, "Error Details"))) | ||
|
||
// Original error in bright red for high visibility | ||
sb.WriteString( | ||
fmt.Sprintf( | ||
"%s: %s\n", | ||
formatWithColor(BrightRed, "Error"), | ||
formatWithColor(Red, te.OriginalError.Error()), | ||
), | ||
) | ||
|
||
// Add user message if available (using cyan for info) | ||
if te.UserMessage != "" { | ||
sb.WriteString( | ||
fmt.Sprintf( | ||
"%s: %s\n", | ||
formatWithColor(BrightCyan, "User Message"), | ||
formatWithColor(Cyan, te.UserMessage), | ||
), | ||
) | ||
} | ||
|
||
// Add stack trace header | ||
sb.WriteString(fmt.Sprintf("\n%s\n", formatWithColor(BrightWhite, "Stack Trace"))) | ||
frames := runtime.CallersFrames(te.StackTrace) | ||
|
||
for { | ||
frame, more := frames.Next() | ||
if !more { | ||
break | ||
} | ||
|
||
// Skip runtime-related functions | ||
if strings.Contains(frame.Function, "runtime.") { | ||
continue | ||
} | ||
|
||
// Function name in bright blue for better visibility | ||
sb.WriteString(fmt.Sprintf("%s\n", formatWithColor(BrightBlue, frame.Function))) | ||
// File and line in dimmed white for less emphasis | ||
sb.WriteString( | ||
fmt.Sprintf( | ||
"\t%s:%d\n", | ||
formatWithColor(White, frame.File), | ||
frame.Line, | ||
), | ||
) | ||
} | ||
|
||
return sb.String() | ||
} | ||
|
||
// WrapError wraps an existing error with additional context, including payload and a user-friendly message. | ||
func WrapError(err error, payload, message string) error { | ||
if err == nil { | ||
return nil | ||
} | ||
|
||
var existingTracedErr *TracedError | ||
if errors.As(err, &existingTracedErr) { | ||
// Merge existing TracedError context | ||
if payload == "" { | ||
payload = existingTracedErr.Payload | ||
} | ||
if message != "" && existingTracedErr.UserMessage != "" { | ||
message = fmt.Sprintf("%s\n%s", existingTracedErr.UserMessage, message) | ||
} else if message == "" { | ||
message = existingTracedErr.UserMessage | ||
} | ||
return &TracedError{ | ||
OriginalError: existingTracedErr.OriginalError, | ||
UserMessage: message, | ||
Payload: payload, | ||
StackTrace: captureCallers(), | ||
} | ||
} | ||
|
||
// Create a new TracedError | ||
return &TracedError{ | ||
OriginalError: err, | ||
UserMessage: message, | ||
Payload: payload, | ||
StackTrace: captureCallers(), | ||
} | ||
} | ||
|
||
// SetEnableColors sets the global EnableColors flag for controlling colored output. | ||
func SetEnableColors(enable bool) { | ||
EnableColors = enable | ||
} |
Oops, something went wrong.