Skip to content

Commit

Permalink
major renovation
Browse files Browse the repository at this point in the history
  • Loading branch information
ryomak committed Apr 27, 2024
1 parent 30984a8 commit fcdfde9
Show file tree
Hide file tree
Showing 22 changed files with 401 additions and 173 deletions.
23 changes: 23 additions & 0 deletions .github/workflows/golangci-lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# ワークフローの名前
name: golangci-lint ReviewDog

on:
pull_request:

# ジョブ定義
jobs:
golangci-lint:
permissions:
checks: write
contents: read
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: reviewdog/action-golangci-lint@v2
with:
github_token: ${{ secrets.github_token }}
go_version: ^1.22
reporter: github-pr-review
level: warning
golangci_lint_flags: "--config=.golangci.yml"
16 changes: 5 additions & 11 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ jobs:
fail-fast: false
matrix:
go:
- '^1.20'
- '^1.21'
- '^1.18'
- '^1.22'
steps:
- name: Check out repository code
uses: actions/checkout@v3
Expand All @@ -25,17 +25,11 @@ jobs:
go-version: ${{ matrix.go }}
- name: vet
run: go vet ./...
- name: Declare some variables
id: vars
run: |
echo "coverage_txt=${RUNNER_TEMP}/coverage.txt" >> "$GITHUB_OUTPUT"
- name: Test Coverage (pkg)
run: go test ./... -race -coverprofile=${{ steps.vars.outputs.coverage_txt }}
- name: Run coverage
run: go test ./... -race -coverprofile=coverage.out -covermode=atomic
- name: Upload coverage reports to Codecov
uses: codecov/[email protected]
if: ${{ matrix.go == '^1.21' }}
with:
token: ${{ secrets.CODECOV_TOKEN }}
slug: ryomak/serrs
- name: Fuzzing serrs package
run: go test . -fuzz=Fuzz -fuzztime=300s
slug: ryomak/serrs
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,5 @@ go.work
.DS_Store

tmp

vendor
14 changes: 14 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
linters:
disable-all: true
enable:
- govet
- errcheck
- staticcheck
- unused
- gosimple
- varcheck
- misspell
- lll
- gofumpt
- paralleltest
- revive
104 changes: 82 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@

[![Go Reference](https://pkg.go.dev/badge/github.com/ryomak/serrs.svg)](https://pkg.go.dev/github.com/ryomak/serrs)
[![GitHub Actions](https://github.com/ryomak/serrs/workflows/test/badge.svg)](https://github.com/ryomak/serrs/actions?query=workflows%3Atest)
[![codecov](https://codecov.io/gh/ryomak/serrs/branch/master/graph/badge.svg)](https://codecov.io/gh/ryomak/serrs)
[![codecov](https://codecov.io/gh/ryomak/serrs/branch/main/graph/badge.svg)](https://codecov.io/gh/ryomak/serrs)
[![Go Report Card](https://goreportcard.com/badge/github.com/ryomak/serrs)](https://goreportcard.com/report/github.com/ryomak/serrs)


# description

serrs is a package that provides a simple error handling mechanism.


serrs is a library designed to simplify error handling in your applications.
By using serrs, developers can effortlessly manage stack traces and integrate with monitoring tools like Sentry.
This library enables you to delegate the complex logic of error handling to serrs, maintaining code readability while also ensuring detailed tracking of errors.

# Installation

Expand All @@ -22,8 +22,13 @@ go get -u github.com/ryomak/serrs
# Usage
## Create an error
```go
var HogeError = serrs.New(serrs.DefaultCode("unexpected"),"unexpected error")
```

var HogeError = serrs.New(serrs.Unexpceted,"unexpected error")
or

```go
var InvalidParameterError = serrs.New(serrs.DefaultCode("invalid_parameter"),"invalid parameter error")
```

## Wrap an error and add a stack trace
Expand All @@ -36,37 +41,92 @@ if err := DoSomething(); err != nil {

fmt.Printf("%+v",err)

// Output:
// Output Example:
// - file: ./serrs/format_test.go:22
// function: github.com/ryomak/serrs_test.TestSerrs_Format
// msg: wrap error
// msg:
// - file: ./serrs/format_test.go:14
// function: github.com/ryomak/serrs_test.TestSerrs_Format
// code: demo
// data: {key1:value1,key2:value2}
// - error1
```

## SendSentry
serrs supports sending errors to Sentry.
### Parameters
The parameters that can be managed with serrs are three: `code`, `message`, and `data`.
`WithXXX` functions can be used to add additional information to the error.
- code: error code
- message: err text
- data: custom data

**data**
`data` is a custom data that can be added to the error. The data is output to the log.
If the type satisfies the CustomData interface, any type can be added.

```go
if err := DoSomething(); err != nil {
return serrs.Wrap(err, serrs.WithData(serrs.DefaultCustomData{
"key": "value",
}))
}
```

### Get Additional Data
- GetCustomData: Get custom data from error
- GetErrorCode: Get error code from error
- GetErrorCodeString: Get error code string from error
- ErrorSurface: Get top level error message
- Origin: Get original error

## check error match
```go
var HogeError = serrs.New(serrs.DefaultCode("unexpected"),"unexpected error")

if serrs.Is(HogeError) {

}
```

## Send Sentry
supports sending reports to Sentry.
The location where serrs.Wrap is executed is saved as a stack trace and displayed cleanly on Sentry. In addition, any added custom data or messages are also displayed as additional data on Sentry.

```go

serrs.ReportSentry(
err,
serrs.WithSentryContexts(map[string]sentry.Context{
"custom": map[string]any{
"key": "value",
},
}),
serrs.WithSentryTags(map[string]string{
"code": serrs.GetErrorCodeString(err),
}),
serrs.WithSentryLevel(sentry.LevelInfo),
err,
// Customize the contexts
serrs.WithSentryContexts(map[string]sentry.Context{
"custom": map[string]any{
"key": "value",
},
}),
// Customize the Sentry tags
serrs.WithSentryTags(map[string]string{
"code": serrs.GetErrorCodeString(err),
}),
// Customize the Sentry Level
serrs.WithSentryLevel(sentry.LevelInfo),
)
```

or

```go
event := serrs.GenerateSentryEvent(err)
sentry.CaptureEvent(event)
```

import (
"github.com/getsentry/sentry-go"
)

func main() {
sentry.Init(sentry.ClientOptions{
Dsn: "your-dsn",
})
defer sentry.Flush(2 * time.Second)

if err := DoSomething(); err != nil {
event := serrs.GenerateSentryEvent(err)
sentry.CaptureEvent(event)
}
}
```
10 changes: 3 additions & 7 deletions code.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,9 @@ type Code interface {
ErrorCode() string
}

// StringCode is a type that represents an error code as a string.
type StringCode string
// DefaultCode is a type that represents an error code as a string.
type DefaultCode string

func (s StringCode) ErrorCode() string {
func (s DefaultCode) ErrorCode() string {
return string(s)
}

const (
StringCodeUnexpected StringCode = "unexpected"
)
8 changes: 4 additions & 4 deletions code_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,22 @@ import (
"github.com/ryomak/serrs"
)

func TestStringCode_ErrorCode(t *testing.T) {
func TestDefaultCode_ErrorCode(t *testing.T) {
tests := []struct {
name string
s serrs.StringCode
s serrs.DefaultCode
want string
}{
{
name: "unexpected",
s: serrs.StringCodeUnexpected,
s: serrs.DefaultCode("unexpected"),
want: "unexpected",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := tt.s.ErrorCode(); got != tt.want {
t.Errorf("StringCode.GetErrorCode() = %v, want %v", got, tt.want)
t.Errorf("DefaultCode.GetErrorCode() = %v, want %v", got, tt.want)
}
})
}
Expand Down
15 changes: 10 additions & 5 deletions error.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ type simpleError struct {
// cause is the cause of the error
cause error

// frame is the location where the error occurred
frame Frame
// frames is the location where the error occurred
frames Frames

// data is the custom data attached to the error
data CustomData
Expand All @@ -32,7 +32,7 @@ type simpleError struct {
func newSimpleError(msg string, skip int) *simpleError {
e := new(simpleError)
e.message = msg
e.frame = caller(skip + 1)
e.frames = caller(skip + 1)
return e
}

Expand Down Expand Up @@ -63,9 +63,14 @@ func (s *simpleError) Error() string {

func (s *simpleError) Is(target error) bool {
if targetErr := asSimpleError(target); targetErr != nil {
return targetErr.getCode() == s.getCode()
targetCode := targetErr.getCode()
sCode := s.getCode()
if targetCode != nil && sCode != nil {
return targetCode.ErrorCode() == sCode.ErrorCode()
}
}
return s == target

return false
}

func (s *simpleError) Unwrap() error {
Expand Down
12 changes: 8 additions & 4 deletions wrapper.go → error_wrapper.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package serrs

type wrapper interface {
// errWrapper is a function that adds information to the error.
type errWrapper interface {
wrap(err *simpleError)
}

func WithCode(code Code) wrapper {
// WithCode returns an error wrapper that adds a code to the error.
func WithCode(code Code) errWrapper {
return codeWrapper{code: code}
}

Expand All @@ -16,7 +18,8 @@ func (c codeWrapper) wrap(err *simpleError) {
_ = err.withCode(c.code)
}

func WithMessage(msg string) wrapper {
// WithMessage returns an error wrapper that adds a message to the error.
func WithMessage(msg string) errWrapper {
return messageWrapper{message: msg}
}

Expand All @@ -28,7 +31,8 @@ func (m messageWrapper) wrap(err *simpleError) {
_ = err.withMessage(m.message)
}

func WithCustomData(data CustomData) wrapper {
// WithData returns an error wrapper that adds custom data to the error.
func WithData(data CustomData) errWrapper {
return customDataWrapper{data: data}
}

Expand Down
12 changes: 7 additions & 5 deletions example/send_sentry/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
"github.com/ryomak/serrs"
)

const DefaultCodeUnexpected serrs.DefaultCode = "unexpected"

func main() {

err := sentry.Init(sentry.ClientOptions{
Expand Down Expand Up @@ -38,7 +40,7 @@ func main() {
}

func Do() error {
return serrs.New(serrs.StringCode("do_err"), "unexpected do error")
return serrs.New(serrs.DefaultCode("do_err"), "unexpected do error")
}

func Do2() error {
Expand All @@ -52,9 +54,9 @@ func Do3() error {
if err := Do2(); err != nil {
return serrs.Wrap(
err,
serrs.WithCode(serrs.StringCodeUnexpected),
serrs.WithCode(DefaultCodeUnexpected),
serrs.WithMessage("do2 error"),
serrs.WithCustomData(serrs.DefaultCustomData{
serrs.WithData(serrs.DefaultCustomData{
"key": "value",
}),
)
Expand All @@ -66,9 +68,9 @@ func Do4() error {
if err := Do3(); err != nil {
return serrs.Wrap(
err,
serrs.WithCode(serrs.StringCodeUnexpected),
serrs.WithCode(DefaultCodeUnexpected),
serrs.WithMessage("Do4 error"),
serrs.WithCustomData(serrs.DefaultCustomData{
serrs.WithData(serrs.DefaultCustomData{
"userName": "hoge",
}),
)
Expand Down
Loading

0 comments on commit fcdfde9

Please sign in to comment.