项目作者: corebreaker

项目描述 :
Easy error informations and stack traces for Go with rich informations and a Try/Catch/Finally mechanism.
高级语言: Go
项目地址: git://github.com/corebreaker/goerrors.git
创建时间: 2016-11-05T19:17:46Z
项目社区:https://github.com/corebreaker/goerrors

开源协议:GNU General Public License v3.0

下载


GoErrors - Easy error informations and stack traces for Go with rich informations and a Try/Catch/Finally mechanism.

Report
Build Status
Coverage Status
GoDoc
GitHub license
Release
Dependency status

It’s a package that’s allow you to make Go errors more comprehensive, more featured and easier to use.
This project is the realisation of the idea introduced by the GIST try-catch-finally.go.

Features

  • Verbose with stack trace
  • Ready for Try/Catch/Finally paradigm
  • Extensible with your own error
  • Error hierarchy
  • Entirely customizable

Installation

go get github.com/corebreaker/goerrors

How is it work ?

A normal error is just an interface. But here we added an extended interface IError which can transport other infomations.
Therefore to add informations, the standard error (error interface) is «decorated» with informations and so
we obtain a new error value. These informations are:

  • stack trace informations (files + lines)
  • additionnal customized string
  • optionnal error code

When the Error method is called, all these informations will be formated in a string.

This type of error gives you rich informations on an error without using panic function (even if with a panic, you
could show your additionnal infomations).

How to use it ?

Decorate error to add a stack trace

  1. // for example, let's open a file
  2. func OpenMyFile() (*os.File, error) {
  3. file, err := os.Open("MyFile.txt")
  4. if err != nil {
  5. // Here, we decorate the error
  6. return nil, goerrors.DecorateError(err)
  7. }
  8. return file, nil
  9. }

Then, we can panic this decorated error or simply print it, like this:

  1. func main() {
  2. // First we must enable the debug mode to activate the stacktrace
  3. goerrors.SetDebug(true)
  4. // Use a function that use the `goerrors` package
  5. file, err := OpenMyFile()
  6. // Check the error
  7. if err != nil {
  8. // Show the error
  9. fmt.Println(err)
  10. // Terminate
  11. return
  12. }
  13. // …
  14. }

You will see some thing like this:

  1. github.com/corebreaker/goerrors.tStandardError: open MyFile.txt: no such file or directory
  2. github.com/corebreaker/goerrors.(*GoError).Init (/home/frederic/go/src/github.com/corebreaker/goerrors/errors.go:219)
  3. github.com/corebreaker/goerrors.DecorateError (/home/frederic/go/src/github.com/corebreaker/goerrors/standard.go:51)
  4. main.OpenMyFile (/home/frederic/.local/share/data/liteide/liteide/goplay.go:13)
  5. main.main (/home/frederic/.local/share/data/liteide/liteide/goplay.go:24)
  6. ------------------------------------------------------------------------------

A Try/Catch/Finally mechanism

Plus, this library uses the panic() function, the recover() function and the defer instruction,
as a Throw and a Try/Catch/Finally mechanisms and can be used like this:

  1. goerrors.Try(func(err goerrors.IError) error {
  2. // Try block
  3. }, func(err goerrors.IError) error {
  4. // Catch block
  5. }, func(err goerrors.IError) error {
  6. // Finally block
  7. })

The error passed in Try block is the error which called the Try method:

  1. theError := goerrors.MakeError("the error in `Try` block")
  2. theError.Try(func(err goerrors.IError) error {
  3. // Here err === theError
  4. return nil
  5. })

In the case in using the Try function in the GoError package, the error passed as argument is an error created by the
call the Try function. Then, that error can be customized with the GoError API.

An example with a throw, called “raise” here

Actually, returning an error in the Try block is a Throw, and a Go panic call is too like a throw but there is
a panic-like function for keeping Try/Catch formalism, the Raise function, used like that:

  1. goerrors.Try(func(err goerrors.IError) error {
  2. if aCondition {
  3. // `Raise` call
  4. goerrors.Raise("an error")
  5. }
  6. // Do something
  7. return nil
  8. }, func(err goerrors.IError) error {
  9. // Catch block
  10. }, func(err goerrors.IError) error {
  11. // Finally block
  12. })

At last, all errors generated by GoError have a Raise method. So, you can throw an error like that:

  1. goerrors.Try(func(err goerrors.IError) error {
  2. if aCondition {
  3. // `Raise` method call
  4. goerrors.MakeError("an error").Raise()
  5. }
  6. // Do something
  7. return nil
  8. }, func(err goerrors.IError) error {
  9. // Catch block
  10. }, func(err goerrors.IError) error {
  11. // Finally block
  12. })

A simple example

  1. package main
  2. import (
  3. "fmt"
  4. gerr "github.com/corebreaker/goerrors"
  5. )
  6. // A function which return checked quotient
  7. func my_func(i, j int) (int, error) {
  8. if j == 0 {
  9. return 0, gerr.MakeError("Division by zero")
  10. }
  11. return i / j, nil
  12. }
  13. // Main function
  14. func main() {
  15. // Activate stack trace
  16. gerr.SetDebug(true)
  17. i, j := 1, 0
  18. // Call checked function
  19. q, err := my_func(i, j)
  20. if err != nil {
  21. fmt.Println(err)
  22. return
  23. }
  24. // Here, in this example, this code won't never be executed
  25. fmt.Print(i, "/", j, "=", q)
  26. }

This will show:

  1. StandardError: Division by zero
  2. main.my_func (/projects/go/prototype/main.go:11)
  3. main.main (/projects/go/prototype/main.go:23)
  4. ------------------------------------------------------------------------------

Another example with existing error

  1. package main
  2. import (
  3. "fmt"
  4. "os"
  5. gerr "github.com/corebreaker/goerrors"
  6. )
  7. // A function which open a file
  8. func openFile(name string) (*os.File, error) {
  9. f, err := os.Open(name)
  10. // Decorate the opening error
  11. if err != nil {
  12. return nil, gerr.DecorateError(err)
  13. }
  14. return f, nil
  15. }
  16. // A function which read one byte in the opened file
  17. func readFile(f *os.File) (byte, error) {
  18. var b [1]byte
  19. n, err := f.Read(b[:])
  20. // Decorate the read error
  21. if err != nil {
  22. return 0, gerr.DecorateError(err)
  23. }
  24. // Return custom error
  25. if n == 0 {
  26. return 0, gerr.MakeError("No data to read")
  27. }
  28. return b[0], nil
  29. }
  30. // Main function
  31. func main() {
  32. // Activate stack trace
  33. gerr.SetDebug(true)
  34. // Call the checked open function
  35. f, err := openFile("a_file.txt")
  36. if err != nil {
  37. fmt.Println(err)
  38. return
  39. }
  40. // Here, in this example, this code won't never be executed if the file can't be opened
  41. defer f.Close()
  42. _, err = readFile(f)
  43. }

This will show:

  1. StandardError: open a_file.txt: no such file or directory
  2. main.open_file (/projects/go/src/github.com/corebreaker/goerrors.go:15)
  3. main.main (/projects/go/src/github.com/corebreaker/goerrors.go:46)
  4. ------------------------------------------------------------------------------

A Try/Catch example with error inheritance

  1. package main
  2. import (
  3. "fmt"
  4. gerr "github.com/corebreaker/goerrors"
  5. )
  6. type ErrorBase struct{ gerr.GoError }
  7. type ErrorA struct{ ErrorBase }
  8. type ErrorB struct{ ErrorBase }
  9. type ChildError struct{ ErrorA }
  10. func newErrorBase() gerr.IError {
  11. err := &ErrorBase{}
  12. return err.Init(err, "message from Error base", nil, nil, 0)
  13. }
  14. func newErrorB() gerr.IError {
  15. err := &ErrorB{}
  16. return err.Init(err, "message from Error B", nil, nil, 0)
  17. }
  18. func newChildError() gerr.IError {
  19. err := &ChildError{}
  20. return err.Init(err, "message from Child Error", nil, nil, 0)
  21. }
  22. // A function which raise and try to catch the error which is not in the same hierarchy
  23. func myFunc() () {
  24. _ = newErrorB().Try(func(err gerr.IError) error {
  25. newChildError().Raise()
  26. return nil
  27. }, func(err gerr.IError) error {
  28. // This catch block will not called because ErrorB is not in the same error hierarchy of ChildError
  29. return nil
  30. }, nil)
  31. }
  32. // Main function
  33. func main() {
  34. _ = newErrorBase().Try(func(err gerr.IError) error {
  35. myFunc()
  36. return nil
  37. }, func(err gerr.IError) error {
  38. fmt.Println("Catched error:", err)
  39. return nil
  40. }, nil)
  41. }

This will show:

  1. Catched error: main.ChildError: message from Child Error