errors

package module
v0.2.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 27, 2023 License: Apache-2.0 Imports: 11 Imported by: 0

README

errors

Go Doc Go Report License

Package error provides a simple error info binding in RESTful service. error definition follow the Google API design.

go get github.com/gota33/errors

Adding status & error_detail to an error

// import . "github.com/gota33/errors"

_, err := db.QueryRow("SELECT * FROM cats WHERE name = 'white' LIMIT 1")

if err != nil {
    detail := ResourceInfo{
        ResourceType: "pet.com/pet.v1.Cat",
        ResourceName: "cat123@pet.com",
        Owner:        "user123",
        Description:  "cat123 not found",
    }
    
    // Standard way
    err = Annotate(err, NotFound, detail)
    
    // Or use predifined wrapper
    err = WithNotFound(err, detail)
    return
}

Retrieving the code of an error

// ...
code := Code(err)
fmt.Println(code)
// Output: 404 NOT_FOUND

Check temporary

// ...
fmt.Println(Temporary(OK))
fmt.Println(Temporary(Internal))
fmt.Println(Temporary(Unavailable))
fmt.Println(Temporary(&net.DNSError{IsTemporary: true}))

// Output:
// false
// false
// true
// true

Retrieving the details of an error

// ...
for _, detail := range Details(err) {
    fmt.Printf("%+v", detail)
}

// Output:
// type: "type.googleapis.com/google.rpc.ResourceInfo"
// resource_type: "pet.com/pet.v1.Cat"
// resource_name: "cat123@pet.com"
// owner: "user123"
// description: "cat123 not found"

Print formatted message

// ...
err := Annotate(
    context.DeadlineExceeded,
    DeadlineExceeded,
    StackTrace("heavy job"),
    RequestInfo{RequestId: "<uuid>"},
    LocalizedMessage{Local: "en-US", Message: "Background task timeout"},
    LocalizedMessage{Local: "zh-CN", Message: "后台任务超时"},
)

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

// Output:
// status: "504 DEADLINE_EXCEEDED"
// message: "context deadline exceeded"
// detail[0]:
// 	type: "type.googleapis.com/google.rpc.DebugInfo"
// 	detail: "heavy job"
// 	stack:
// 		goroutine 1 [running]:
// 		runtime/debug.Stack(0xc00005e980, 0x40, 0x40)
// 			/home/user/go/src/runtime/debug/stack.go:24 +0xa5
// 		github.com/gota33/errors.StackTrace.Annotate(0xfe36af, 0x9, 0x1056490, 0xc00005e980)
// 			/home/user/github/gota33/errors/detail.go:368 +0x2d
// 		github.com/gota33/errors.Annotate(0x1051780, 0x1257e60, 0xc00010fc00, 0x5, 0x5, 0xc00010fba8, 0x10)
// 			/home/user/github/gota33/errors/errors.go:79 +0x97
// 		github.com/gota33/errors.ExampleAnnotate()
// 			/home/user/github/gota33/errors/example_test.go:10 +0x251
// 		testing.runExample(0xfe589a, 0xf, 0xfff6c0, 0xfead08, 0x1a, 0x0, 0x0)
// 			/home/user/go/src/testing/run_example.go:63 +0x222
// 		testing.runExamples(0xc00010fed0, 0x120aee0, 0x3, 0x3, 0x0)
// 			/home/user/go/src/testing/example.go:44 +0x185
// 		testing.(*M).Run(0xc000114100, 0x0)
// 			/home/user/go/src/testing/testing.go:1419 +0x27d
// 		main.main()
// 			_testmain.go:71 +0x145
//
// detail[1]:
// 	type: "type.googleapis.com/google.rpc.RequestInfo"
// 	request_id: "<uuid>"
// 	serving_data: ""
// detail[2]:
// 	type: "type.googleapis.com/google.rpc.LocalizedMessage"
// 	local: "en-US"
// 	message: "Background task timeout"
// detail[3]:
// 	type: "type.googleapis.com/google.rpc.LocalizedMessage"
// 	local: "zh-CN"
// 	message: "后台任务超时"

Encode error to JSON

// ...
// Use any JSON encoder you prefered
jEnc := json.NewEncoder(os.Stdout)
jEnc.SetIndent("  ", "  ")
enc := NewEncoder(jEnc)
// Hide DebugInfo before send to client
enc.Filters = []DetailFilter{HideDebugInfo}
_ = enc.Encode(err)

// Example Output:
// {
//    "error": {
//      "code": 504,
//      "message": "context deadline exceeded",
//      "status": "DEADLINE_EXCEEDED",
//      "details": [
//        {
//          "@type": "type.googleapis.com/google.rpc.RequestInfo",
//          "requestId": "\u003cuuid\u003e"
//        },
//        {
//          "@type": "type.googleapis.com/google.rpc.LocalizedMessage",
//          "local": "en-US",
//          "message": "Background task timeout"
//        },
//        {
//          "@type": "type.googleapis.com/google.rpc.LocalizedMessage",
//          "local": "zh-CN",
//          "message": "后台任务超时"
//        }
//      ]
//    }
//  }

Decode error from JSON

Decode manually
// import . "github.com/gota33/errors"
client := &http.Client{}
resp, err := client.Get("https://localhost:8080/users/1")
if err != nil {
    return err
}
defer resp.Body.Close()

// Decode from response body
dec := NewDecoder(json.NewDecoder(resp.Body))

err := dec.Decode()

fmt.Println(Code(err))
// Output: 404 NOT_FOUND

Decode automatically
// import . "github.com/gota33/errors"
client := http.Client{
    Transport: &RoundTripper{
        Parent: http.DefaultTransport,
    },
}

resp, err := client.Get("https://localhost:8080/users/1")
if err == nil {
    defer resp.Body.Close()
}

fmt.Println(Code(err))
// Output: 404 NOT_FOUND

Documentation

Index

Examples

Constants

View Source
const (
	TypeUrlRetryInfo           = typeUrlPrefix + "RetryInfo"
	TypeUrlDebugInfo           = typeUrlPrefix + "DebugInfo"
	TypeUrlResourceInfo        = typeUrlPrefix + "ResourceInfo"
	TypeUrlBadRequest          = typeUrlPrefix + "BadRequest"
	TypeUrlPreconditionFailure = typeUrlPrefix + "PreconditionFailure"
	TypeUrlErrorInfo           = typeUrlPrefix + "ErrorInfo"
	TypeUrlQuotaFailure        = typeUrlPrefix + "QuotaFailure"
	TypeUrlRequestInfo         = typeUrlPrefix + "RequestInfo"
	TypeUrlHelp                = typeUrlPrefix + "Help"
	TypeUrlLocalizedMessage    = typeUrlPrefix + "LocalizedMessage"
)

Variables

View Source
var (
	ErrNoEncoder = errors.New("encoder: inner encoder is required")
	ErrNoDecoder = errors.New("decoder: inner decoder is required")
)
View Source
var (
	New    = errors.New
	As     = errors.As
	Is     = errors.Is
	Unwrap = errors.Unwrap
	Join   = errors.Join
)

Functions

func Annotate

func Annotate(cause error, annotations ...Annotation) error
Example
err := Annotate(
	context.DeadlineExceeded,
	DeadlineExceeded,
	StackTrace("heavy job"),
	RequestInfo{RequestId: "<uuid>"},
	LocalizedMessage{Local: "en-US", Message: "Background task timeout"},
	LocalizedMessage{Local: "zh-CN", Message: "后台任务超时"},
)

fmt.Printf("%+v", err)
// Example Output:
// status: "504 DEADLINE_EXCEEDED"
// message: "context deadline exceeded"
// detail[0]:
// 	type: "type.googleapis.com/google.rpc.DebugInfo"
// 	detail: "heavy job"
// 	stack:
// 		goroutine 1 [running]:
// 		runtime/debug.Stack(0xc00005e980, 0x40, 0x40)
// 			/home/user/go/src/runtime/debug/stack.go:24 +0xa5
// 		github.com/gota33/errors.StackTrace.Annotate(0xfe36af, 0x9, 0x1056490, 0xc00005e980)
// 			/home/user/github/gota33/errors/detail.go:368 +0x2d
// 		github.com/gota33/errors.Annotate(0x1051780, 0x1257e60, 0xc00010fc00, 0x5, 0x5, 0xc00010fba8, 0x10)
// 			/home/user/github/gota33/errors/errors.go:79 +0x97
// 		github.com/gota33/errors.ExampleAnnotate()
// 			/home/user/github/gota33/errors/example_test.go:10 +0x251
// 		testing.runExample(0xfe589a, 0xf, 0xfff6c0, 0xfead08, 0x1a, 0x0, 0x0)
// 			/home/user/go/src/testing/run_example.go:63 +0x222
// 		testing.runExamples(0xc00010fed0, 0x120aee0, 0x3, 0x3, 0x0)
// 			/home/user/go/src/testing/example.go:44 +0x185
// 		testing.(*M).Run(0xc000114100, 0x0)
// 			/home/user/go/src/testing/testing.go:1419 +0x27d
// 		main.main()
// 			_testmain.go:71 +0x145
//
// detail[1]:
// 	type: "type.googleapis.com/google.rpc.RequestInfo"
// 	request_id: "<uuid>"
// 	serving_data: ""
// detail[2]:
// 	type: "type.googleapis.com/google.rpc.LocalizedMessage"
// 	local: "en-US"
// 	message: "Background task timeout"
// detail[3]:
// 	type: "type.googleapis.com/google.rpc.LocalizedMessage"
// 	local: "zh-CN"
// 	message: "后台任务超时"

func Flatten

func Flatten(err error, mappers ...DetailMapper) error

func Register added in v0.1.3

func Register(typeUrl string, provider func() Any)

func Temporary added in v0.1.6

func Temporary(err error) (out bool)
Example
fmt.Println(Temporary(OK))
fmt.Println(Temporary(Internal))
fmt.Println(Temporary(Unavailable))
fmt.Println(Temporary(&net.DNSError{IsTemporary: true}))
Output:

false
false
true
true

func WithAborted added in v0.1.4

func WithAborted(cause error, detail ErrorInfo) (err error)

func WithAlreadyExists added in v0.1.4

func WithAlreadyExists(cause error, detail ResourceInfo) (err error)

func WithBadRequest added in v0.1.4

func WithBadRequest(cause error, detail BadRequest) error

func WithCancelled added in v0.1.4

func WithCancelled(cause error) (err error)

func WithDataLoss added in v0.1.4

func WithDataLoss(cause error, detail DebugInfo) (err error)

func WithDeadlineExceeded added in v0.1.4

func WithDeadlineExceeded(cause error, detail DebugInfo) (err error)

func WithFailedPrecondition added in v0.1.4

func WithFailedPrecondition(cause error, detail PreconditionFailure) error

func WithInternal added in v0.1.4

func WithInternal(cause error, detail DebugInfo) (err error)

func WithNotFound added in v0.1.4

func WithNotFound(cause error, detail ResourceInfo) error

func WithOutOfRange added in v0.1.4

func WithOutOfRange(cause error, detail BadRequest) (err error)

func WithPermissionDenied added in v0.1.4

func WithPermissionDenied(cause error, detail ErrorInfo) (err error)

func WithResourceExhausted added in v0.1.4

func WithResourceExhausted(cause error, detail QuotaFailure) (err error)

func WithUnauthenticated added in v0.1.4

func WithUnauthenticated(cause error, detail ErrorInfo) (err error)

func WithUnavailable added in v0.1.4

func WithUnavailable(cause error, detail DebugInfo) (err error)

func WithUnimplemented added in v0.1.4

func WithUnimplemented(cause error) (err error)

func WithUnknown added in v0.1.4

func WithUnknown(cause error, detail DebugInfo) (err error)

Types

type Annotation

type Annotation interface {
	Annotate(m Modifier)
}

type Any

type Any interface {
	Annotation
	TypeUrl() string
}

func Details

func Details(err error) (out []Any)
Example
client := &http.Client{}
client.Get("")
err := WithNotFound(sql.ErrNoRows, ResourceInfo{
	ResourceType: "pet.com/pet.v1.Cat",
	ResourceName: "cat123@pet.com",
	Owner:        "user123",
	Description:  "cat123 not found",
})

for _, detail := range Details(err) {
	fmt.Printf("%+v", detail)
}
Output:

type: "type.googleapis.com/google.rpc.ResourceInfo"
resource_type: "pet.com/pet.v1.Cat"
resource_name: "cat123@pet.com"
owner: "user123"
description: "cat123 not found"

func HideDebugInfo added in v0.1.4

func HideDebugInfo(a Any) Any

type AnyDetail

type AnyDetail map[string]interface{}

func (AnyDetail) Annotate added in v0.1.4

func (d AnyDetail) Annotate(m Modifier)

func (AnyDetail) MarshalJSON

func (d AnyDetail) MarshalJSON() ([]byte, error)

func (AnyDetail) TypeUrl

func (d AnyDetail) TypeUrl() string

type BadRequest

type BadRequest struct {
	FieldViolations []FieldViolation `json:"fieldViolations,omitempty"`
}

func (BadRequest) Annotate added in v0.1.3

func (d BadRequest) Annotate(m Modifier)

func (BadRequest) Format

func (d BadRequest) Format(f fmt.State, verb rune)

func (BadRequest) MarshalJSON

func (d BadRequest) MarshalJSON() ([]byte, error)

func (BadRequest) TypeUrl

func (d BadRequest) TypeUrl() string

type DebugInfo

type DebugInfo struct {
	StackEntries []string `json:"stackEntries,omitempty"`
	Detail       string   `json:"detail,omitempty"`
}

func (DebugInfo) Annotate added in v0.1.3

func (d DebugInfo) Annotate(m Modifier)

func (DebugInfo) Format

func (d DebugInfo) Format(f fmt.State, verb rune)

func (DebugInfo) MarshalJSON

func (d DebugInfo) MarshalJSON() ([]byte, error)

func (DebugInfo) TypeUrl

func (d DebugInfo) TypeUrl() string

type Decoder added in v0.1.4

type Decoder struct {
	// contains filtered or unexported fields
}

func NewDecoder added in v0.1.4

func NewDecoder(dec decoder) *Decoder

func (Decoder) Decode added in v0.1.4

func (d Decoder) Decode() (err error)

type DetailMapper added in v0.1.5

type DetailMapper func(a Any) Any

type Duration added in v0.1.7

type Duration time.Duration

func (Duration) MarshalJSON added in v0.1.7

func (d Duration) MarshalJSON() ([]byte, error)

func (*Duration) UnmarshalJSON added in v0.1.7

func (d *Duration) UnmarshalJSON(data []byte) (err error)

type Encoder added in v0.1.4

type Encoder struct {
	Mappers []DetailMapper
	// contains filtered or unexported fields
}
Example
err := Annotate(
	context.DeadlineExceeded,
	DeadlineExceeded,
	StackTrace("heavy job"),
	RequestInfo{RequestId: "<uuid>"},
	LocalizedMessage{Local: "en-US", Message: "Background task timeout"},
	LocalizedMessage{Local: "zh-CN", Message: "后台任务超时"},
)

jEnc := json.NewEncoder(os.Stdout)
jEnc.SetIndent("  ", "  ")
enc := NewEncoder(jEnc)
enc.Mappers = []DetailMapper{HideDebugInfo}
_ = enc.Encode(err)

// Example Output:
// {
//    "error": {
//      "code": 504,
//      "message": "context deadline exceeded",
//      "status": "DEADLINE_EXCEEDED",
//      "details": [
//        {
//          "@type": "type.googleapis.com/google.rpc.RequestInfo",
//          "requestId": "\u003cuuid\u003e"
//        },
//        {
//          "@type": "type.googleapis.com/google.rpc.LocalizedMessage",
//          "local": "en-US",
//          "message": "Background task timeout"
//        },
//        {
//          "@type": "type.googleapis.com/google.rpc.LocalizedMessage",
//          "local": "zh-CN",
//          "message": "后台任务超时"
//        }
//      ]
//    }
//  }

func NewEncoder added in v0.1.4

func NewEncoder(enc encoder) *Encoder

func (*Encoder) Encode added in v0.1.4

func (e *Encoder) Encode(in error) error

type ErrorInfo

type ErrorInfo struct {
	Reason   string            `json:"reason,omitempty"`
	Domain   string            `json:"domain,omitempty"`
	Metadata map[string]string `json:"metadata,omitempty"`
}

func (ErrorInfo) Annotate added in v0.1.3

func (d ErrorInfo) Annotate(m Modifier)

func (ErrorInfo) Format

func (d ErrorInfo) Format(f fmt.State, verb rune)

func (ErrorInfo) MarshalJSON

func (d ErrorInfo) MarshalJSON() ([]byte, error)

func (ErrorInfo) TypeUrl

func (d ErrorInfo) TypeUrl() string

type FieldViolation

type FieldViolation struct {
	Field       string `json:"field,omitempty"`
	Description string `json:"description,omitempty"`
}

type Help

type Help struct {
	Links []Link `json:"links,omitempty"`
}

func (Help) Annotate added in v0.1.3

func (d Help) Annotate(m Modifier)

func (Help) Format

func (d Help) Format(f fmt.State, verb rune)

func (Help) MarshalJSON

func (d Help) MarshalJSON() ([]byte, error)

func (Help) TypeUrl

func (d Help) TypeUrl() string
type Link struct {
	Description string `json:"description,omitempty"`
	Url         string `json:"url,omitempty"`
}

type LocalizedMessage

type LocalizedMessage struct {
	Local   string `json:"local,omitempty"`
	Message string `json:"message,omitempty"`
}

func (LocalizedMessage) Annotate added in v0.1.3

func (d LocalizedMessage) Annotate(m Modifier)

func (LocalizedMessage) Format

func (d LocalizedMessage) Format(f fmt.State, verb rune)

func (LocalizedMessage) MarshalJSON

func (d LocalizedMessage) MarshalJSON() ([]byte, error)

func (LocalizedMessage) TypeUrl

func (d LocalizedMessage) TypeUrl() string

type Message added in v0.1.3

type Message string

func (Message) Annotate added in v0.1.3

func (a Message) Annotate(m Modifier)

type Modifier added in v0.1.3

type Modifier interface {
	SetCode(code StatusCode)
	WrapMessage(msg string)
	AppendDetails(details ...Any)
}

type PreconditionFailure

type PreconditionFailure struct {
	Violations []TypedViolation `json:"violations,omitempty"`
}

func (PreconditionFailure) Annotate added in v0.1.3

func (d PreconditionFailure) Annotate(m Modifier)

func (PreconditionFailure) Format

func (d PreconditionFailure) Format(f fmt.State, verb rune)

func (PreconditionFailure) MarshalJSON

func (d PreconditionFailure) MarshalJSON() ([]byte, error)

func (PreconditionFailure) TypeUrl

func (d PreconditionFailure) TypeUrl() string

type QuotaFailure

type QuotaFailure struct {
	Violations []Violation `json:"violations,omitempty"`
}

func (QuotaFailure) Annotate added in v0.1.3

func (d QuotaFailure) Annotate(m Modifier)

func (QuotaFailure) Format

func (d QuotaFailure) Format(f fmt.State, verb rune)

func (QuotaFailure) MarshalJSON

func (d QuotaFailure) MarshalJSON() ([]byte, error)

func (QuotaFailure) TypeUrl

func (d QuotaFailure) TypeUrl() string

type RequestInfo

type RequestInfo struct {
	RequestId   string `json:"requestId,omitempty"`
	ServingData string `json:"servingData,omitempty"`
}

func (RequestInfo) Annotate added in v0.1.3

func (d RequestInfo) Annotate(m Modifier)

func (RequestInfo) Format

func (d RequestInfo) Format(f fmt.State, verb rune)

func (RequestInfo) MarshalJSON

func (d RequestInfo) MarshalJSON() ([]byte, error)

func (RequestInfo) TypeUrl

func (d RequestInfo) TypeUrl() string

type ResourceInfo

type ResourceInfo struct {
	ResourceType string `json:"resourceType,omitempty"`
	ResourceName string `json:"resourceName,omitempty"`
	Owner        string `json:"owner,omitempty"`
	Description  string `json:"description,omitempty"`
}

func (ResourceInfo) Annotate added in v0.1.3

func (d ResourceInfo) Annotate(m Modifier)

func (ResourceInfo) Format

func (d ResourceInfo) Format(f fmt.State, verb rune)

func (ResourceInfo) MarshalJSON

func (d ResourceInfo) MarshalJSON() ([]byte, error)

func (ResourceInfo) TypeUrl

func (d ResourceInfo) TypeUrl() string

type RetryInfo added in v0.1.7

type RetryInfo struct {
	RetryDelay Duration `json:"retryDelay"`
}

func (RetryInfo) Annotate added in v0.1.7

func (d RetryInfo) Annotate(m Modifier)

func (RetryInfo) Format added in v0.1.7

func (d RetryInfo) Format(f fmt.State, verb rune)

func (RetryInfo) MarshalJSON added in v0.1.7

func (d RetryInfo) MarshalJSON() ([]byte, error)

func (RetryInfo) TypeUrl added in v0.1.7

func (d RetryInfo) TypeUrl() string

type RoundTripper added in v0.1.1

type RoundTripper struct {
	Parent http.RoundTripper
}

func (*RoundTripper) RoundTrip added in v0.1.1

func (e *RoundTripper) RoundTrip(req *http.Request) (resp *http.Response, err error)

type StackTrace added in v0.1.3

type StackTrace string

func (StackTrace) Annotate added in v0.1.3

func (s StackTrace) Annotate(m Modifier)

type StatusCode

type StatusCode int
const (
	OK StatusCode = iota
	Cancelled
	Unknown
	InvalidArgument
	DeadlineExceeded
	NotFound
	AlreadyExists
	PermissionDenied
	ResourceExhausted
	FailedPrecondition
	Aborted
	OutOfRange
	Unimplemented
	Internal
	Unavailable
	DataLoss
	Unauthenticated
)

func Code

func Code(err error) (out StatusCode)
Example
err := WithNotFound(sql.ErrNoRows, ResourceInfo{
	ResourceType: "pet.com/pet.v1.Cat",
	ResourceName: "cat123@pet.com",
	Owner:        "user123",
	Description:  "cat123 not found",
})
code := Code(err)
fmt.Println(code)
Output:

404 NOT_FOUND

func (StatusCode) Annotate added in v0.1.3

func (c StatusCode) Annotate(m Modifier)

func (StatusCode) Error

func (c StatusCode) Error() string

func (StatusCode) Http added in v0.1.4

func (c StatusCode) Http() int

func (StatusCode) Name added in v0.1.4

func (c StatusCode) Name() StatusName

func (StatusCode) String

func (c StatusCode) String() string

func (StatusCode) Temporary added in v0.1.6

func (c StatusCode) Temporary() bool

func (StatusCode) Valid

func (c StatusCode) Valid() bool

type StatusName added in v0.1.4

type StatusName string

func (StatusName) StatusCode added in v0.1.4

func (s StatusName) StatusCode() StatusCode

func (StatusName) String added in v0.1.4

func (s StatusName) String() string

type TypedViolation

type TypedViolation struct {
	Type        string `json:"type,omitempty"`
	Subject     string `json:"subject,omitempty"`
	Description string `json:"description,omitempty"`
}

type Violation

type Violation struct {
	Subject     string `json:"subject,omitempty"`
	Description string `json:"description,omitempty"`
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL